diff --git a/.gitignore b/.gitignore
index 524f2e6d0..db16295ad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -249,8 +249,6 @@ widthspec.bin
/pata_test
/po/*.gmo
/po/*.mo
-/po/*.po
-/po/LINGUAS
/po/Makefile.in.in
/po/Makevars
/po/Makevars.template
@@ -283,3 +281,4 @@ widthspec.bin
/xfs_test
/xzcompress_test
/zfs_test
+/zfs_zstd_test
diff --git a/BUGS b/BUGS
index 46faa6452..09efa95dd 100644
--- a/BUGS
+++ b/BUGS
@@ -1,7 +1,3 @@
-GRUB team is aware of following problems:
- - Currently search and assembling multidevice abstractions scans
- all the devices which can be slow.
- - Cache isn't used correctly for video which results in slowness.
+Open bugs and issues are captured in the GRUB bug tracking system:
-While these are bugs their solution has a potential of breaking more and more
-seriously. So it was decided for 1.99 that they aren't fixed.
+ https://savannah.gnu.org/bugs/?group=grub
diff --git a/INSTALL b/INSTALL
index 724584c57..7ac1c7cc8 100644
--- a/INSTALL
+++ b/INSTALL
@@ -25,6 +25,7 @@ configuring the GRUB.
* Flex 2.5.35 or later
* pkg-config
* GNU patch
+* wget (for downloading updated translations)
* Other standard GNU/Unix tools
* a libc with large file support (e.g. glibc 2.1 or later)
@@ -45,12 +46,17 @@ To build GRUB's graphical terminal (gfxterm), you need:
To build grub-mkfont the unicode fonts are required (xfonts-unifont package
on Debian).
+To build the optional grub-protect utility the following is needed:
+
+* libtasn1 (libtasn1-6-dev on Debian)
+
If you use a development snapshot or want to hack on GRUB you may
need the following.
* Python 3 (NOTE: python 2.6 should still work, but it's not tested)
* Autoconf 2.64 or later
* Automake 1.14 or later
+* GNU Autoconf Archive (autoconf-archive on Debian)
Your distro may package cross-compiling toolchains such as the following
incomplete list on Debian: gcc-aarch64-linux-gnu, gcc-arm-linux-gnueabihf,
@@ -62,7 +68,7 @@ More cross compiling toolchains can be found at the following trusted sites:
* https://mirrors.kernel.org/pub/tools/crosstool/
* https://toolchains.bootlin.com/
-Prerequisites for make-check:
+Prerequisites for running make-check successfully:
* qemu, specifically the binary "qemu-system-ARCH" where ARCH is the
architecture GRUB has been built for; the "qemu-system" package on Debian
@@ -82,6 +88,8 @@ Prerequisites for make-check:
reiserfs, udf, xfs
- On newer kernels, the exfat kernel modules may be used instead of the
exfat FUSE filesystem
+ - Kernel version 6.12.x was the last series to include reiserfs support,
+ so later kernels will fail the reiser filesystem test.
* The following are Debian named packages required mostly for the full
suite of filesystem testing (but some are needed by other tests as well):
- btrfs-progs, dosfstools, e2fsprogs, erofs-utils, exfatprogs, exfat-fuse,
@@ -92,8 +100,8 @@ Prerequisites for make-check:
- attr, cpio, g++, gawk, parted, recode, tar, util-linux
Note that `make check' will run and many tests may complete successfully
-with only a subset of these prerequisites. However, some tests may be
-skipped or fail due to missing prerequisites.
+with only a subset of these prerequisites. However, some tests may fail
+due to missing prerequisites.
To build the documentation you'll need:
* texinfo, for the info and html documentation
@@ -136,15 +144,12 @@ The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code.
- 2. Skip this and following step if you use release tarball and proceed to
- step 4. If you want translations type `./linguas.sh'.
-
- 3. Type `./bootstrap'.
+ 2. Type `./bootstrap'.
- The autogen.sh (called by bootstrap) uses python. By default autodetect
- it, but it can be overridden by setting the PYTHON variable.
+ The autogen.sh (called by bootstrap) uses python. By default it is
+ autodetected, but it can be overridden by setting the PYTHON variable.
- 4. Type `./configure' to configure the package for your system.
+ 3. Type `./configure' to configure the package for your system.
If you're using `csh' on an old version of System V, you might
need to type `sh ./configure' instead to prevent `csh' from trying
to execute `configure' itself.
@@ -152,19 +157,19 @@ The simplest way to compile this package is:
Running `configure' takes awhile. While running, it prints some
messages telling which features it is checking for.
- 6. Type `make' to compile the package.
+ 4. Type `make' to compile the package.
- 7. Optionally, type `make check' to run any self-tests that come with
- the package. Note that many of the tests require root privileges in
- order to run.
+ 5. Optionally, type `make check' to run any self-tests that come with
+ the package. Note that many of the tests require root privileges and
+ a specially crafted environment in order to run.
- 8. Type `make install' to install the programs and any data files and
+ 6. Type `make install' to install the programs and any data files and
documentation.
- 9. Type `make html' or `make pdf' to generate the html or pdf
+ 7. Type `make html' or `make pdf' to generate the html or pdf
documentation. Note, these are not built by default.
- 10. You can remove the program binaries and object files from the
+ 8. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
diff --git a/Makefile.util.def b/Makefile.util.def
index 038253b37..313dd0a68 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -36,6 +36,7 @@ library = {
common = grub-core/kern/misc.c;
common = grub-core/kern/partition.c;
common = grub-core/lib/crypto.c;
+ common = grub-core/lib/hwfeatures-gcry.c;
common = grub-core/lib/json/json.c;
common = grub-core/disk/luks.c;
common = grub-core/disk/luks2.c;
@@ -43,6 +44,7 @@ library = {
common = grub-core/disk/key_protector.c;
common = grub-core/disk/cryptodisk.c;
common = grub-core/disk/AFSplitter.c;
+ common = grub-core/lib/argon2.c;
common = grub-core/lib/pbkdf2.c;
common = grub-core/commands/extcmd.c;
common = grub-core/lib/arg.c;
@@ -162,6 +164,7 @@ library = {
common = grub-core/io/gzio.c;
common = grub-core/io/xzio.c;
common = grub-core/io/lzopio.c;
+ common = grub-core/io/zstdio.c;
common = grub-core/kern/ia64/dl_helper.c;
common = grub-core/kern/arm/dl_helper.c;
common = grub-core/kern/arm64/dl_helper.c;
@@ -201,8 +204,8 @@ program = {
extra_dist = util/grub-mkimagexx.c;
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBLZMA)';
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
@@ -225,8 +228,8 @@ program = {
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 = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBTASN1)';
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
@@ -243,8 +246,8 @@ program = {
common = grub-core/osdep/init.c;
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -258,8 +261,8 @@ program = {
common = grub-core/osdep/init.c;
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -284,8 +287,8 @@ program = {
ldadd = '$(LIBLZMA)';
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -300,8 +303,8 @@ program = {
common = grub-core/osdep/init.c;
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -323,8 +326,8 @@ program = {
common = grub-core/osdep/init.c;
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -341,8 +344,8 @@ program = {
cflags = '$(FUSE_CFLAGS)';
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM) $(FUSE_LIBS)';
condition = COND_GRUB_MOUNT;
@@ -359,8 +362,8 @@ program = {
cppflags = '-DGRUB_MKFONT=1';
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(FREETYPE_LIBS)';
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
@@ -378,8 +381,8 @@ program = {
common = grub-core/osdep/init.c;
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -436,8 +439,8 @@ program = {
common = grub-core/osdep/init.c;
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -451,8 +454,8 @@ program = {
common = grub-core/osdep/init.c;
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -466,8 +469,8 @@ program = {
common = grub-core/kern/emu/argp_common.c;
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -605,8 +608,8 @@ program = {
ldadd = '$(LIBLZMA)';
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
@@ -652,8 +655,8 @@ program = {
ldadd = '$(LIBLZMA)';
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -691,8 +694,8 @@ program = {
ldadd = '$(LIBLZMA)';
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -727,8 +730,8 @@ program = {
ldadd = '$(LIBLZMA)';
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -911,6 +914,12 @@ script = {
common = tests/zfs_test.in;
};
+script = {
+ testcase = native;
+ name = zfs_zstd_test;
+ common = tests/zfs_zstd_test.in;
+};
+
script = {
testcase = native;
name = cpio_test;
@@ -1285,13 +1294,13 @@ script = {
};
script = {
- testcase = native;
+ testcase = nonnative;
name = asn1_test;
common = tests/asn1_test.in;
};
script = {
- testcase = native;
+ testcase = nonnative;
name = tpm2_key_protector_test;
common = tests/tpm2_key_protector_test.in;
};
@@ -1305,8 +1314,8 @@ program = {
common = grub-core/kern/misc.c;
common = grub-core/tests/lib/test.c;
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -1320,8 +1329,8 @@ program = {
common = grub-core/kern/misc.c;
common = grub-core/tests/lib/test.c;
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -1335,8 +1344,8 @@ program = {
common = grub-core/kern/misc.c;
common = grub-core/tests/lib/test.c;
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -1351,8 +1360,8 @@ program = {
common = grub-core/tests/lib/test.c;
common = grub-core/lib/priority_queue.c;
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
condition = COND_HAVE_CXX;
@@ -1367,8 +1376,8 @@ program = {
common = grub-core/kern/misc.c;
common = grub-core/tests/lib/test.c;
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -1382,8 +1391,8 @@ program = {
common = grub-core/osdep/init.c;
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -1400,8 +1409,8 @@ program = {
common = grub-core/kern/emu/argp_common.c;
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -1416,8 +1425,8 @@ program = {
common = grub-core/osdep/init.c;
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -1434,8 +1443,8 @@ program = {
common = grub-core/osdep/init.c;
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
@@ -1463,8 +1472,8 @@ program = {
common = grub-core/osdep/init.c;
ldadd = libgrubmods.a;
- ldadd = libgrubgcry.a;
ldadd = libgrubkern.a;
+ ldadd = libgrubgcry.a;
ldadd = grub-core/lib/gnulib/libgnu.a;
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
diff --git a/NEWS b/NEWS
index 310130962..13a8f490f 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,26 @@
+New in 2.14:
+
+* libgcrypt 1.11.
+* LVM LV integrity and cachevol support.
+* EROFS support.
+* GRUB environment block inside the Btrfs header support.
+* NX support for EFI platforms.
+* shim loader protocol support.
+* BLS and UKI support.
+* Argon2 KDF support.
+* TPM2 key protector support.
+* Appended Signature Secure Boot Support for PowerPC.
+* New option to block command line interface.
+* Support dates outside of 1901..2038 range.
+* zstdio decompression support.
+* EFI code improvements and fixes.
+* TPM driver fixes.
+* Filesystems fixes.
+* CVE and Coverity fixes.
+* Tests improvements.
+* Documentation improvements.
+* ... and tons of other fixes and cleanups...
+
New in 2.12:
* GCC 13 support.
diff --git a/acinclude.m4 b/acinclude.m4
index fa7840f09..70c1912f8 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -79,6 +79,11 @@ AC_DEFUN([grub_PROG_OBJCOPY_ABSOLUTE],
[AC_MSG_CHECKING([whether ${TARGET_OBJCOPY} works for absolute addresses])
AC_CACHE_VAL(grub_cv_prog_objcopy_absolute,
[cat > conftest.c <<\EOF
+asm (
+ ".globl start, _start, __start\n"
+ ".ifdef cmain; .set start = _start = __start = cmain\n.endif\n"
+ ".ifdef _cmain; .set start = _start = __start = _cmain\n.endif\n"
+);
void cmain (void);
void
cmain (void)
diff --git a/autogen.sh b/autogen.sh
index fbdb33879..7dd26cd33 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -52,6 +52,17 @@ 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
+for x in sha256-ssse3-amd64.S sha256-avx-amd64.S sha256-avx2-bmi2-amd64.S sha256-intel-shaext.c sha512-ssse3-amd64.S sha512-avx-amd64.S sha512-avx2-bmi2-amd64.S sha512-avx512-amd64.S; do
+ if [ -h grub-core/lib/libgcrypt-grub/cipher/"$x" ] || [ -f grub-core/lib/libgcrypt-grub/cipher/"$x" ]; then
+ rm grub-core/lib/libgcrypt-grub/cipher/"$x"
+ fi
+ cp grub-core/lib/libgcrypt/cipher/"$x" grub-core/lib/libgcrypt-grub/cipher/"$x"
+done
+
+if [ -f grub-core/lib/libgcrypt-grub/src/hwfeatures.c ]; then
+ rm grub-core/lib/libgcrypt-grub/src/hwfeatures.c
+fi
+
for x in grub-core/lib/libgcrypt-patches/*.patch; do
patch -i $x -p1
done
diff --git a/bootstrap b/bootstrap
index dc2238f4a..dc9fb4383 100755
--- a/bootstrap
+++ b/bootstrap
@@ -1,34 +1,67 @@
#! /bin/sh
-# Print a version string.
-scriptversion=2022-01-26.05; # UTC
+# DO NOT EDIT! GENERATED AUTOMATICALLY!
# Bootstrap this package from checked-out sources.
-# Copyright (C) 2003-2022 Free Software Foundation, Inc.
+scriptversion=2025-06-10.02; # UTC
+# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+#
# 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 .
# Originally written by Paul Eggert. The canonical version of this
-# script is maintained as build-aux/bootstrap in gnulib, however, to
-# be useful to your project, you should place a copy of it under
-# version control in the top-level directory of your project. The
+# script is maintained as top/bootstrap in gnulib. However, to be
+# useful to your package, you should place a copy of it under version
+# control in the top-level directory of your package. The intent is
+# that all customization can be done with a bootstrap.conf file also
+# maintained in your version control; gnulib comes with a template
+# build-aux/bootstrap.conf to get you started.
+
+# Please report bugs or propose patches to bug-gnulib@gnu.org.
+
+me="$0"
+medir=`dirname "$me"`
+
+# Read the function library and the configuration.
+
+# A library of shell functions for autopull.sh, autogen.sh, and bootstrap.
+
+scriptlibversion=2025-06-10.02; # UTC
+
+# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+#
+# 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 .
+
+# Originally written by Paul Eggert. The canonical version of this
+# script is maintained as top/bootstrap-funclib.sh in gnulib. However,
+# to be useful to your package, you should place a copy of it under
+# version control in the top-level directory of your package. The
# intent is that all customization can be done with a bootstrap.conf
# file also maintained in your version control; gnulib comes with a
# template build-aux/bootstrap.conf to get you started.
-# Please report bugs or propose patches to bug-gnulib@gnu.org.
-
nl='
'
@@ -36,86 +69,13 @@ nl='
LC_ALL=C
export LC_ALL
-# Ensure that CDPATH is not set. Otherwise, the output from cd
-# would cause trouble in at least one use below.
-(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
-
-local_gl_dir=gl
-
# Honor $PERL, but work even if there is none.
PERL="${PERL-perl}"
-me=$0
-
default_gnulib_url=https://git.savannah.gnu.org/git/gnulib.git
-usage() {
- cat </dev/null)
+normalize_package_name='
+ s/^GNU //
+ y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
+ s/[^abcdefghijklmnopqrstuvwxyz0123456789_]/-/g
+'
+package=$(${AUTOCONF:-autoconf} --trace 'AC_INIT:$4' configure.ac 2>/dev/null)
if test -z "$package"; then
package=$(sed -n "$extract_package_name" configure.ac) \
|| die 'cannot find package name in configure.ac'
fi
+package=$(echo "$package" | sed "$normalize_package_name")
gnulib_name=lib$package
build_aux=build-aux
@@ -247,15 +214,13 @@ COPYRIGHT_HOLDER='Free Software Foundation, Inc.'
MSGID_BUGS_ADDRESS=bug-$package@gnu.org
# Files we don't want to import.
+# XXX Not used.
excluded_files=
# File that should exist in the top directory of a checked out hierarchy,
# but not in a distribution tarball.
checkout_only_file=README-hacking
-# Whether to use copies instead of symlinks.
-copy=false
-
# Set this to '.cvsignore .gitignore' in bootstrap.conf if you want
# those files to be generated in directories like lib/, m4/, and po/.
# Or set it to 'auto' to make this script select which to use based
@@ -266,8 +231,13 @@ vc_ignore=auto
# default.
bootstrap_sync=false
-# Use git to update gnulib sources
-use_git=true
+# Override the default configuration, if necessary.
+# Make sure that bootstrap.conf is sourced from the current directory
+# if we were invoked as "sh bootstrap".
+conffile=`dirname "$me"`/bootstrap.conf
+test -r "$conffile" && . "$conffile"
+
+# ------------------------- Build-time prerequisites -------------------------
check_exists() {
if test "$1" = "--verbose"; then
@@ -284,6 +254,202 @@ check_exists() {
test $? -lt 126
}
+# Note this deviates from the version comparison in automake
+# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a
+# but this should suffice as we won't be specifying old
+# version formats or redundant trailing .0 in bootstrap.conf.
+# If we did want full compatibility then we should probably
+# use m4_version_compare from autoconf.
+sort_ver() { # sort -V is not generally available
+ ver1="$1"
+ ver2="$2"
+
+ # split on '.' and compare each component
+ i=1
+ while : ; do
+ p1=$(echo "$ver1" | cut -d. -f$i)
+ p2=$(echo "$ver2" | cut -d. -f$i)
+ if [ ! "$p1" ]; then
+ echo "$1 $2"
+ break
+ elif [ ! "$p2" ]; then
+ echo "$2 $1"
+ break
+ elif [ ! "$p1" = "$p2" ]; then
+ if [ "$p1" -gt "$p2" ] 2>/dev/null; then # numeric comparison
+ echo "$2 $1"
+ elif [ "$p2" -gt "$p1" ] 2>/dev/null; then # numeric comparison
+ echo "$1 $2"
+ else # numeric, then lexicographic comparison
+ lp=$(printf "%s\n%s\n" "$p1" "$p2" | LANG=C sort -n | tail -n1)
+ if [ "$lp" = "$p2" ]; then
+ echo "$1 $2"
+ else
+ echo "$2 $1"
+ fi
+ fi
+ break
+ fi
+ i=$(($i+1))
+ done
+}
+
+get_version_sed='
+# Move version to start of line.
+s/.*[v ]\([0-9]\)/\1/
+
+# Skip lines that do not start with version.
+/^[0-9]/!d
+
+# Remove characters after the version.
+s/[^.a-z0-9-].*//
+
+# The first component must be digits only.
+s/^\([0-9]*\)[a-z-].*/\1/
+
+#the following essentially does s/5.005/5.5/
+s/\.0*\([1-9]\)/.\1/g
+p
+q'
+
+get_version() {
+ app=$1
+
+ $app --version >/dev/null 2>&1 || { $app --version; return 1; }
+
+ $app --version 2>&1 | sed -n "$get_version_sed"
+}
+
+check_versions() {
+ ret=0
+
+ while read app req_ver; do
+ # We only need libtoolize from the libtool package.
+ if test "$app" = libtool; then
+ app=libtoolize
+ fi
+ # Exempt git if git is not needed.
+ if test "$app" = git; then
+ $check_git || continue
+ fi
+ # Honor $APP variables ($TAR, $AUTOCONF, etc.)
+ appvar=$(echo $app | LC_ALL=C tr '[a-z]-' '[A-Z]_')
+ test "$appvar" = TAR && appvar=AMTAR
+ case $appvar in
+ GZIP) ;; # Do not use $GZIP: it contains gzip options.
+ PERL::*) ;; # Keep perl modules as-is
+ *) eval "app=\${$appvar-$app}" ;;
+ esac
+
+ # Handle the still-experimental Automake-NG programs specially.
+ # They remain named as the mainstream Automake programs ("automake",
+ # and "aclocal") to avoid gratuitous incompatibilities with
+ # preexisting usages (by, say, autoreconf, or custom autogen.sh
+ # scripts), but correctly identify themselves (as being part of
+ # "GNU automake-ng") when asked their version.
+ case $app in
+ automake-ng|aclocal-ng)
+ app=${app%-ng}
+ ($app --version | grep '(GNU automake-ng)') >/dev/null 2>&1 || {
+ warn_ "Error: '$app' not found or not from Automake-NG"
+ ret=1
+ continue
+ } ;;
+ # Another check is for perl modules. These can be written as
+ # e.g. perl::XML::XPath in case of XML::XPath module, etc.
+ perl::*)
+ # Extract module name
+ app="${app#perl::}"
+ if ! $PERL -m"$app" -e 'exit 0' >/dev/null 2>&1; then
+ warn_ "Error: perl module '$app' not found"
+ ret=1
+ fi
+ continue
+ ;;
+ esac
+ if [ "$req_ver" = "-" ]; then
+ # Merely require app to exist; not all prereq apps are well-behaved
+ # so we have to rely on $? rather than get_version.
+ if ! check_exists --verbose $app; then
+ warn_ "Error: '$app' not found"
+ ret=1
+ fi
+ else
+ # Require app to produce a new enough version string.
+ inst_ver=$(get_version $app)
+ if [ ! "$inst_ver" ]; then
+ warn_ "Error: '$app' not found"
+ ret=1
+ else
+ latest_ver=$(sort_ver $req_ver $inst_ver | cut -d' ' -f2)
+ if [ ! "$latest_ver" = "$inst_ver" ]; then
+ warnf_ '%s\n' \
+ "Error: '$app' version == $inst_ver is too old" \
+ " '$app' version >= $req_ver is required"
+ ret=1
+ fi
+ fi
+ fi
+ done
+
+ return $ret
+}
+
+print_versions() {
+ echo "Program Min_version"
+ echo "----------------------"
+ printf %s "$buildreq"
+ echo "----------------------"
+ # can't depend on column -t
+}
+
+# check_build_prerequisites check_git
+check_build_prerequisites()
+{
+ check_git="$1"
+
+ # gnulib-tool requires at least automake and autoconf.
+ # If either is not listed, add it (with minimum version) as a prerequisite.
+ case $buildreq in
+ *automake*) ;;
+ *) buildreq="automake 1.9
+$buildreq" ;;
+ esac
+ case $buildreq in
+ *autoconf*) ;;
+ *) buildreq="autoconf 2.59
+$buildreq" ;;
+ esac
+
+ # When we can deduce that gnulib-tool will require patch,
+ # and when patch is not already listed as a prerequisite, add it, too.
+ if test -d "$local_gl_dir" \
+ && ! find "$local_gl_dir" -name '*.diff' -exec false {} +; then
+ case $buildreq in
+ *patch*) ;;
+ *) buildreq="patch -
+$buildreq" ;;
+ esac
+ fi
+
+ if ! printf '%s' "$buildreq" | check_versions; then
+ echo >&2
+ if test -f README-prereq; then
+ die "See README-prereq for how to get the prerequisite programs"
+ else
+ die "Please install the prerequisite programs"
+ fi
+ fi
+
+ # Warn the user if autom4te appears to be broken; this causes known
+ # issues with at least gettext 0.18.3.
+ probe=$(echo 'm4_quote([hi])' | autom4te -l M4sugar -t 'm4_quote:$%' -)
+ if test "x$probe" != xhi; then
+ warn_ "WARNING: your autom4te wrapper eats stdin;"
+ warn_ "if bootstrap fails, consider upgrading your autotools"
+ fi
+}
+
# find_tool ENVVAR NAMES...
# -------------------------
# Search for a required program. Use the value of ENVVAR, if set,
@@ -313,6 +479,530 @@ find_tool ()
eval "export $find_tool_envvar"
}
+# --------------------- Preparing GNULIB_SRCDIR for use. ---------------------
+# This is part of autopull.sh, but bootstrap needs it too, for self-upgrading.
+
+# cleanup_gnulib fails, removing the directory $gnulib_path first.
+cleanup_gnulib() {
+ status=$?
+ rm -fr "$gnulib_path"
+ exit $status
+}
+
+git_modules_config () {
+ test -f .gitmodules && git config --file .gitmodules "$@"
+}
+
+prepare_GNULIB_SRCDIR ()
+{
+ if test -n "$GNULIB_SRCDIR"; then
+ # Use GNULIB_SRCDIR directly.
+ # We already checked that $GNULIB_SRCDIR references a directory.
+ # Verify that it contains a gnulib checkout.
+ test -f "$GNULIB_SRCDIR/gnulib-tool" \
+ || die "Error: --gnulib-srcdir or \$GNULIB_SRCDIR is specified," \
+ "but does not contain gnulib-tool"
+ if test -n "$GNULIB_REVISION" && $use_git; then
+ # The 'git checkout "$GNULIB_REVISION"' command succeeds if the
+ # GNULIB_REVISION is a commit hash that exists locally, or if it is
+ # branch name that can be fetched from origin. It fails, however,
+ # if the GNULIB_REVISION is a commit hash that only exists in
+ # origin. In this case, we need a 'git fetch' and then retry
+ # 'git checkout "$GNULIB_REVISION"'.
+ git -C "$GNULIB_SRCDIR" checkout "$GNULIB_REVISION" 2>/dev/null \
+ || { git -C "$GNULIB_SRCDIR" fetch origin \
+ && git -C "$GNULIB_SRCDIR" checkout "$GNULIB_REVISION"; } \
+ || exit $?
+ fi
+ else
+ if ! $use_git; then
+ die "Error: --no-git is specified," \
+ "but neither --gnulib-srcdir nor \$GNULIB_SRCDIR is specified"
+ fi
+ if git submodule -h | grep -- --reference > /dev/null; then
+ :
+ else
+ die "git version is too old, git >= 1.6.4 is required"
+ fi
+ gnulib_path=$(git_modules_config submodule.gnulib.path)
+ if test -n "$gnulib_path"; then
+ # A submodule 'gnulib' is configured.
+ # Get gnulib files. Populate $gnulib_path, updating the submodule.
+ if test -n "$GNULIB_REFDIR" && test -d "$GNULIB_REFDIR"/.git; then
+ # Use GNULIB_REFDIR as a reference.
+ echo "$0: getting gnulib files..."
+ git submodule update --init --reference "$GNULIB_REFDIR" "$gnulib_path"\
+ || exit $?
+ else
+ # GNULIB_REFDIR is not set or not usable. Ignore it.
+ if git_modules_config submodule.gnulib.url >/dev/null; then
+ echo "$0: getting gnulib files..."
+ git submodule init -- "$gnulib_path" || exit $?
+ git submodule update -- "$gnulib_path" || exit $?
+ else
+ die "Error: submodule 'gnulib' has no configured url"
+ fi
+ fi
+ else
+ gnulib_path='gnulib'
+ if test ! -d "$gnulib_path"; then
+ # The subdirectory 'gnulib' does not yet exist. Clone into it.
+ echo "$0: getting gnulib files..."
+ trap cleanup_gnulib HUP INT PIPE TERM
+ gnulib_url=${GNULIB_URL:-$default_gnulib_url}
+ if test -n "$GNULIB_REFDIR" && test -d "$GNULIB_REFDIR"/.git; then
+ # Use GNULIB_REFDIR as a reference.
+ git clone "$GNULIB_REFDIR" "$gnulib_path" \
+ && git -C "$gnulib_path" remote set-url origin "$gnulib_url" \
+ && if test -z "$GNULIB_REVISION"; then
+ git -C "$gnulib_path" pull origin \
+ && {
+ # We want the default branch of "$gnulib_url" (since that's
+ # the behaviour if GNULIB_REFDIR is not specified), not the
+ # current branch of "$GNULIB_REFDIR".
+ default_branch=`LC_ALL=C git -C "$gnulib_path" \
+ remote show origin \
+ | sed -n -e 's/^ *HEAD branch: //p'`
+ test -n "$default_branch" || default_branch='master'
+ git -C "$gnulib_path" checkout "$default_branch"
+ }
+ else
+ # The 'git checkout "$GNULIB_REVISION"' command succeeds if the
+ # GNULIB_REVISION is a commit hash that exists locally, or if it
+ # is a branch name that can be fetched from origin. It fails,
+ # however, if the GNULIB_REVISION is a commit hash that only
+ # exists in origin. In this case, we need a 'git fetch' and then
+ # retry 'git checkout "$GNULIB_REVISION"'.
+ git -C "$gnulib_path" checkout "$GNULIB_REVISION" 2>/dev/null \
+ || { git -C "$gnulib_path" fetch origin \
+ && git -C "$gnulib_path" checkout "$GNULIB_REVISION"; }
+ fi \
+ || cleanup_gnulib
+ else
+ # GNULIB_REFDIR is not set or not usable. Ignore it.
+ shallow='--depth 2'
+ if test -z "$GNULIB_REVISION"; then
+ git clone $shallow "$gnulib_url" "$gnulib_path" \
+ || cleanup_gnulib
+ else
+ # Only want a shallow checkout of $GNULIB_REVISION, but git does not
+ # support cloning by commit hash. So attempt a shallow fetch by
+ # commit hash to minimize the amount of data downloaded and changes
+ # needed to be processed, which can drastically reduce download and
+ # processing time for checkout. If the fetch by commit fails, a
+ # shallow fetch cannot be performed because we do not know what the
+ # depth of the commit is without fetching all commits. So fall back
+ # to fetching all commits.
+ # $GNULIB_REVISION can be a commit id, a tag name, or a branch name.
+ mkdir -p "$gnulib_path"
+ # Use a -c option to silence an annoying message
+ # "hint: Using 'master' as the name for the initial branch."
+ # (cf. ).
+ git -C "$gnulib_path" -c init.defaultBranch=master init
+ git -C "$gnulib_path" remote add origin "$gnulib_url"
+ if git -C "$gnulib_path" fetch $shallow origin "$GNULIB_REVISION"
+ then
+ # "git fetch" of the specific commit succeeded.
+ git -C "$gnulib_path" reset --hard FETCH_HEAD \
+ || cleanup_gnulib
+ # "git fetch" does not fetch tags (at least in git version 2.43).
+ # If $GNULIB_REVISION is a tag (not a commit id or branch name),
+ # add the tag explicitly.
+ revision=`git -C "$gnulib_path" log -1 --pretty=format:%H`
+ branch=`LC_ALL=C git -C "$gnulib_path" remote show origin \
+ | sed -n -e 's/^ \([^ ]*\) * tracked$/\1/p'`
+ test "$revision" = "$GNULIB_REVISION" \
+ || test "$branch" = "$GNULIB_REVISION" \
+ || git -C "$gnulib_path" tag "$GNULIB_REVISION"
+ else
+ # Fetch the entire repository.
+ git -C "$gnulib_path" fetch origin \
+ || cleanup_gnulib
+ git -C "$gnulib_path" checkout "$GNULIB_REVISION" \
+ || cleanup_gnulib
+ fi
+ fi
+ fi
+ trap - HUP INT PIPE TERM
+ else
+ # The subdirectory 'gnulib' already exists.
+ if test -n "$GNULIB_REVISION"; then
+ if test -d "$gnulib_path/.git"; then
+ # The 'git checkout "$GNULIB_REVISION"' command succeeds if the
+ # GNULIB_REVISION is a commit hash that exists locally, or if it
+ # is a branch name that can be fetched from origin. It fails,
+ # however, if the GNULIB_REVISION is a commit hash that only
+ # exists in origin. In this case, we need a 'git fetch' and then
+ # retry 'git checkout "$GNULIB_REVISION"'.
+ git -C "$gnulib_path" checkout "$GNULIB_REVISION" 2>/dev/null \
+ || { git -C "$gnulib_path" fetch origin \
+ && git -C "$gnulib_path" checkout "$GNULIB_REVISION"; } \
+ || exit $?
+ else
+ die "Error: GNULIB_REVISION is specified in bootstrap.conf," \
+ "but '$gnulib_path' contains no git history"
+ fi
+ fi
+ fi
+ fi
+ # Verify that $gnulib_path contains a gnulib checkout.
+ test -f "$gnulib_path/gnulib-tool" \
+ || die "Error: '$gnulib_path' is supposed to contain a gnulib checkout," \
+ "but does not contain gnulib-tool"
+ GNULIB_SRCDIR=$gnulib_path
+ fi
+ # $GNULIB_SRCDIR now points to the version of gnulib to use, and
+ # we no longer need to use git or $gnulib_path below here.
+}
+
+# -------- Upgrading bootstrap to the version found in GNULIB_SRCDIR. --------
+
+upgrade_bootstrap ()
+{
+ if test -f "$medir"/bootstrap-funclib.sh; then
+ update_lib=true
+ { cmp -s "$medir"/bootstrap "$GNULIB_SRCDIR/top/bootstrap" \
+ && cmp -s "$medir"/bootstrap-funclib.sh \
+ "$GNULIB_SRCDIR/top/bootstrap-funclib.sh" \
+ && cmp -s "$medir"/autopull.sh "$GNULIB_SRCDIR/top/autopull.sh" \
+ && cmp -s "$medir"/autogen.sh "$GNULIB_SRCDIR/top/autogen.sh"; \
+ }
+ else
+ update_lib=false
+ cmp -s "$medir"/bootstrap "$GNULIB_SRCDIR/build-aux/bootstrap"
+ fi || {
+ if $update_lib; then
+ echo "$0: updating bootstrap & companions and restarting..."
+ else
+ echo "$0: updating bootstrap and restarting..."
+ fi
+ case $(sh -c 'echo "$1"' -- a) in
+ a) ignored=--;;
+ *) ignored=ignored;;
+ esac
+ u=$update_lib
+ exec sh -c \
+ '{ if '$u' && test -f "$1"; then cp "$1" "$3"; else cp "$2" "$3"; fi; } &&
+ { if '$u' && test -f "$4"; then cp "$4" "$5"; else rm -f "$5"; fi; } &&
+ { if '$u' && test -f "$6"; then cp "$6" "$7"; else rm -f "$7"; fi; } &&
+ { if '$u' && test -f "$8"; then cp "$8" "$9"; else rm -f "$9"; fi; } &&
+ shift && shift && shift && shift && shift &&
+ shift && shift && shift && shift &&
+ exec "${CONFIG_SHELL-/bin/sh}" "$@"' \
+ $ignored \
+ "$GNULIB_SRCDIR/top/bootstrap" "$GNULIB_SRCDIR/build-aux/bootstrap" \
+ "$medir/bootstrap" \
+ "$GNULIB_SRCDIR/top/bootstrap-funclib.sh" "$medir/bootstrap-funclib.sh" \
+ "$GNULIB_SRCDIR/top/autopull.sh" "$medir/autopull.sh" \
+ "$GNULIB_SRCDIR/top/autogen.sh" "$medir/autogen.sh" \
+ "$0" "$@" --no-bootstrap-sync
+ }
+}
+
+# ----------------------------------------------------------------------------
+
+if test x"$gnulib_modules$gnulib_files$gnulib_extra_files" = x; then
+ use_gnulib=false
+else
+ use_gnulib=true
+fi
+
+# -------- Fetch auxiliary files from the network. --------------------------
+
+autopull_usage() {
+ cat </dev/null 2>&1 && unset CDPATH
+
+ # Parse options.
+
+ # Use git to update gnulib sources
+ use_git=true
+
+ for option
+ do
+ case $option in
+ --help)
+ autopull_usage
+ return;;
+ --version)
+ set -e
+ echo "autopull.sh $scriptlibversion"
+ echo "$copyright"
+ return 0
+ ;;
+ --skip-po)
+ SKIP_PO=t;;
+ --force)
+ checkout_only_file=;;
+ --bootstrap-sync)
+ bootstrap_sync=true;;
+ --no-bootstrap-sync)
+ bootstrap_sync=false;;
+ --no-git)
+ use_git=false;;
+ *)
+ bootstrap_option_hook $option || die "$option: unknown option";;
+ esac
+ done
+
+ $use_git || test -n "$GNULIB_SRCDIR" \
+ || die "Error: --no-git requires \$GNULIB_SRCDIR environment variable" \
+ "or --gnulib-srcdir option"
+ test -z "$GNULIB_SRCDIR" || test -d "$GNULIB_SRCDIR" \
+ || die "Error: \$GNULIB_SRCDIR environment variable" \
+ "or --gnulib-srcdir option is specified," \
+ "but does not denote a directory"
+
+ if test -n "$checkout_only_file" && test ! -r "$checkout_only_file"; then
+ die "Running this script from a non-checked-out distribution is risky."
+ fi
+
+ check_build_prerequisites $use_git
+
+ if $use_gnulib || $bootstrap_sync; then
+ prepare_GNULIB_SRCDIR
+ if $bootstrap_sync; then
+ upgrade_bootstrap "$@"
+ fi
+ fi
+
+ # Find sha1sum, named gsha1sum on MacPorts, shasum on Mac OS X 10.6.
+ # Also find the compatible sha1 utility on the BSDs
+ if test x"$SKIP_PO" = x; then
+ find_tool SHA1SUM sha1sum gsha1sum shasum sha1
+ fi
+
+ # See if we can use gnulib's git-merge-changelog merge driver.
+ if $use_git && test -d .git && check_exists git; then
+ if git config merge.merge-changelog.driver >/dev/null ; then
+ :
+ elif check_exists git-merge-changelog; then
+ echo "$0: initializing git-merge-changelog driver"
+ git config merge.merge-changelog.name 'GNU-style ChangeLog merge driver'
+ git config merge.merge-changelog.driver 'git-merge-changelog %O %A %B'
+ else
+ echo "$0: consider installing git-merge-changelog from gnulib"
+ fi
+ fi
+
+ case $SKIP_PO in
+ '')
+ if test -d po; then
+ update_po_files po $package || return
+ fi
+
+ if test -d runtime-po; then
+ update_po_files runtime-po $package-runtime || return
+ fi;;
+ esac
+
+ # ---------------------------------------------------------------------------
+
+ bootstrap_post_pull_hook \
+ || die "bootstrap_post_pull_hook failed"
+
+ # Don't proceed if there are uninitialized submodules. In particular,
+ # autogen.sh will remove dangling links, which might be links into
+ # uninitialized submodules.
+ # But it's OK if the 'gnulib' submodule is uninitialized, as long as
+ # GNULIB_SRCDIR is set.
+ if $use_git; then
+ # Uninitialized submodules are listed with an initial dash.
+ uninitialized=`git submodule | grep '^-' | awk '{ print $2 }'`
+ if test -n "$GNULIB_SRCDIR"; then
+ uninitialized=`echo "$uninitialized" | grep -v '^gnulib$'`
+ fi
+ if test -n "$uninitialized"; then
+ uninit_comma=`echo "$uninitialized" | tr '\n' ',' | sed -e 's|,$|.|'`
+ die "Some git submodules are not initialized: "$uninit_comma \
+ "Either use option '--no-git'," \
+ "or run 'git submodule update --init' and bootstrap again."
+ fi
+ fi
+
+ if test -f "$medir"/autogen.sh; then
+ echo "$0: done. Now you can run '$medir/autogen.sh'."
+ fi
+}
+
+# ----------------------------- Get translations. -----------------------------
+
+download_po_files() {
+ subdir=$1
+ domain=$2
+ echo "$me: getting translations into $subdir for $domain..."
+ cmd=$(printf "$po_download_command_format" "$subdir" "$domain")
+ eval "$cmd"
+}
+
+# Mirror .po files to $po_dir/.reference and copy only the new
+# or modified ones into $po_dir. Also update $po_dir/LINGUAS.
+# Note po files that exist locally only are left in $po_dir but will
+# not be included in LINGUAS and hence will not be distributed.
+update_po_files() {
+ # Directory containing primary .po files.
+ # Overwrite them only when we're sure a .po file is new.
+ po_dir=$1
+ domain=$2
+
+ # Mirror *.po files into this dir.
+ # Usually contains *.s1 checksum files.
+ ref_po_dir="$po_dir/.reference"
+
+ test -d $ref_po_dir || mkdir $ref_po_dir || return
+ download_po_files $ref_po_dir $domain \
+ && ls "$ref_po_dir"/*.po 2>/dev/null |
+ sed 's|.*/||; s|\.po$||' > "$po_dir/LINGUAS" || return
+
+ for po in x $(ls $ref_po_dir | sed -n 's/\.po$//p'); do
+ case $po in x) continue;; esac
+ new_po="$ref_po_dir/$po.po"
+ cksum_file="$ref_po_dir/$po.s1"
+ if ! test -f "$cksum_file" ||
+ ! test -f "$po_dir/$po.po" ||
+ ! $SHA1SUM -c "$cksum_file" < "$new_po" > /dev/null 2>&1; then
+ echo "$me: updated $po_dir/$po.po..."
+ cp "$new_po" "$po_dir/$po.po" \
+ && $SHA1SUM < "$new_po" > "$cksum_file" || return
+ fi
+ done
+}
+
+# -------- Generate files automatically from existing sources. --------------
+
+autogen_usage() {
+ cat < /dev/null 2>&1
+ elif test -d .svn; then
+ svn log -r HEAD "$file" > /dev/null 2>&1
+ elif test -d CVS; then
+ grep -F "/${file##*/}/" "$parent/CVS/Entries" 2>/dev/null |
+ grep '^/[^/]*/[0-9]' > /dev/null
+ else
+ warn_ "no version control for $file?"
+ false
+ fi
+}
+
# Strip blank and comment lines to leave significant entries.
gitignore_entries() {
sed '/^#/d; /^$/d' "$@"
@@ -374,7 +1064,7 @@ symlink_to_dir()
for dot_ig in x $vc_ignore; do
test $dot_ig = x && continue
ig=$parent/$dot_ig
- insert_vc_ignore $ig "${dst_dir##*/}"
+ insert_vc_ignore $ig "${dst_dir##*/}/"
done
fi
@@ -423,30 +1113,426 @@ symlink_to_dir()
}
}
-# Override the default configuration, if necessary.
-# Make sure that bootstrap.conf is sourced from the current directory
-# if we were invoked as "sh bootstrap".
-case "$0" in
- */*) test -r "$0.conf" && . "$0.conf" ;;
- *) test -r "$0.conf" && . ./"$0.conf" ;;
-esac
+# Regenerate all autogeneratable files that are omitted from the
+# version control repository. In particular, regenerate all
+# aclocal.m4, config.h.in, Makefile.in, configure files with new
+# versions of autoconf or automake.
+autogen()
+{
+ # Ensure that CDPATH is not set. Otherwise, the output from cd
+ # would cause trouble in at least one use below.
+ (unset CDPATH) >/dev/null 2>&1 && unset CDPATH
-if test "$vc_ignore" = auto; then
- vc_ignore=
- test -d .git && vc_ignore=.gitignore
- test -d CVS && vc_ignore="$vc_ignore .cvsignore"
-fi
+ # Environment variables that may be set by the user.
+ : "${AUTOPOINT=autopoint}"
+ : "${AUTORECONF=autoreconf}"
-if test x"$gnulib_modules$gnulib_files$gnulib_extra_files" = x; then
- use_gnulib=false
-else
- use_gnulib=true
-fi
+ if test "$vc_ignore" = auto; then
+ vc_ignore=
+ test -d .git && vc_ignore=.gitignore
+ test -d CVS && vc_ignore="$vc_ignore .cvsignore"
+ fi
-# Translate configuration into internal form.
+
+ # Parse options.
+
+ # Whether to use copies instead of symlinks.
+ copy=false
+
+ for option
+ do
+ case $option in
+ --help)
+ autogen_usage
+ return;;
+ --version)
+ set -e
+ echo "autogen.sh $scriptlibversion"
+ echo "$copyright"
+ return 0
+ ;;
+ --force)
+ checkout_only_file=;;
+ --copy)
+ copy=true;;
+ *)
+ bootstrap_option_hook $option || die "$option: unknown option";;
+ esac
+ done
+
+ test -z "$GNULIB_SRCDIR" || test -d "$GNULIB_SRCDIR" \
+ || die "Error: \$GNULIB_SRCDIR environment variable or --gnulib-srcdir" \
+ "option is specified, but does not denote a directory"
+
+ if test -n "$checkout_only_file" && test ! -r "$checkout_only_file"; then
+ die "Running this script from a non-checked-out distribution is risky."
+ fi
+
+ if $use_gnulib; then
+ if test -z "$GNULIB_SRCDIR"; then
+ gnulib_path=$(test -f .gitmodules &&
+ git config --file .gitmodules submodule.gnulib.path)
+ test -z "$gnulib_path" && gnulib_path=gnulib
+ GNULIB_SRCDIR=$gnulib_path
+ fi
+ fi
+
+ # Die if there is no AC_CONFIG_AUX_DIR($build_aux) line in configure.ac.
+ found_aux_dir=no
+ grep '^[ ]*AC_CONFIG_AUX_DIR(\['"$build_aux"'])' configure.ac \
+ >/dev/null && found_aux_dir=yes
+ grep '^[ ]*AC_CONFIG_AUX_DIR('"$build_aux"')' configure.ac \
+ >/dev/null && found_aux_dir=yes
+ test $found_aux_dir = yes \
+ || die "configure.ac lacks 'AC_CONFIG_AUX_DIR([$build_aux])'; add it"
+
+ # If $build_aux doesn't exist, create it now, otherwise some bits
+ # below will malfunction. If creating it, also mark it as ignored.
+ if test ! -d $build_aux; then
+ mkdir $build_aux
+ for dot_ig in x $vc_ignore; do
+ test $dot_ig = x && continue
+ insert_vc_ignore $dot_ig $build_aux/
+ done
+ fi
+
+ check_build_prerequisites false
+
+ use_libtool=0
+ # We'd like to use grep -E, to see if any of LT_INIT,
+ # AC_PROG_LIBTOOL, AM_PROG_LIBTOOL is used in configure.ac,
+ # but that's not portable enough (e.g., for Solaris).
+ grep '^[ ]*A[CM]_PROG_LIBTOOL' configure.ac >/dev/null \
+ && use_libtool=1
+ grep '^[ ]*LT_INIT' configure.ac >/dev/null \
+ && use_libtool=1
+ if test $use_libtool = 1; then
+ find_tool LIBTOOLIZE glibtoolize libtoolize
+ fi
+
+ if $use_gnulib; then
+ gnulib_tool=$GNULIB_SRCDIR/gnulib-tool
+ <$gnulib_tool || return
+ fi
+
+ # NOTE: we have to be careful to run both autopoint and libtoolize
+ # before gnulib-tool, since gnulib-tool is likely to provide newer
+ # versions of files "installed" by these two programs.
+ # Then, *after* gnulib-tool (see below), we have to be careful to
+ # run autoreconf in such a way that it does not run either of these
+ # two just-pre-run programs.
+
+ # Import from gettext.
+ with_gettext=yes
+ grep '^[ ]*AM_GNU_GETTEXT_VERSION(' configure.ac >/dev/null || \
+ with_gettext=no
+
+ if test $with_gettext = yes || test $use_libtool = 1; then
+
+ tempbase=.bootstrap$$
+ trap "rm -f $tempbase.0 $tempbase.1" HUP INT PIPE TERM
+
+ > $tempbase.0 > $tempbase.1 &&
+ find . ! -type d -print | sort > $tempbase.0 || return
+
+ if test $with_gettext = yes; then
+ # Released autopoint has the tendency to install macros that have been
+ # obsoleted in current gnulib, so run this before gnulib-tool.
+ echo "$0: $AUTOPOINT --force"
+ $AUTOPOINT --force || return
+ fi
+
+ # Autoreconf runs aclocal before libtoolize, which causes spurious
+ # warnings if the initial aclocal is confused by the libtoolized
+ # (or worse out-of-date) macro directory.
+ # libtoolize 1.9b added the --install option; but we support back
+ # to libtoolize 1.5.22, where the install action was default.
+ if test $use_libtool = 1; then
+ install=
+ case $($LIBTOOLIZE --help) in
+ *--install*) install=--install ;;
+ esac
+ echo "running: $LIBTOOLIZE $install --copy"
+ $LIBTOOLIZE $install --copy
+ fi
+
+ find . ! -type d -print | sort >$tempbase.1
+ old_IFS=$IFS
+ IFS=$nl
+ for file in $(comm -13 $tempbase.0 $tempbase.1); do
+ IFS=$old_IFS
+ parent=${file%/*}
+ version_controlled_file "$parent" "$file" || {
+ for dot_ig in x $vc_ignore; do
+ test $dot_ig = x && continue
+ ig=$parent/$dot_ig
+ insert_vc_ignore "$ig" "${file##*/}"
+ done
+ }
+ done
+ IFS=$old_IFS
+
+ rm -f $tempbase.0 $tempbase.1
+ trap - HUP INT PIPE TERM
+ fi
+
+ # Import from gnulib.
+
+ if $use_gnulib; then
+ gnulib_tool_options="\
+ --no-changelog\
+ --aux-dir=$build_aux\
+ --doc-base=$doc_base\
+ --lib=$gnulib_name\
+ --m4-base=$m4_base/\
+ --source-base=$source_base/\
+ --tests-base=$tests_base\
+ --local-dir=$local_gl_dir\
+ $gnulib_tool_option_extras\
+ "
+ if test $use_libtool = 1; then
+ case "$gnulib_tool_options " in
+ *' --libtool '*) ;;
+ *) gnulib_tool_options="$gnulib_tool_options --libtool" ;;
+ esac
+ fi
+ echo "$0: $gnulib_tool $gnulib_tool_options --import ..."
+ $gnulib_tool $gnulib_tool_options --import $gnulib_modules \
+ || die "gnulib-tool failed"
+
+ if test $with_gettext = yes && test ! -f $m4_base/gettext.m4; then
+ # The gnulib-tool invocation has removed $m4_base/gettext.m4, that the
+ # AUTOPOINT invocation had installed. This can occur when the gnulib
+ # module 'gettext' was previously present but is now not present any more.
+ # Repeat the AUTOPOINT invocation and the gnulib-tool invocation.
+
+ echo "$0: $AUTOPOINT --force"
+ $AUTOPOINT --force || return
+
+ echo "$0: $gnulib_tool $gnulib_tool_options --import ..."
+ $gnulib_tool $gnulib_tool_options --import $gnulib_modules \
+ || die "gnulib-tool failed"
+ fi
+
+ for file in $gnulib_files; do
+ symlink_to_dir "$GNULIB_SRCDIR" $file \
+ || die "failed to symlink $file"
+ done
+ fi
+
+ bootstrap_post_import_hook \
+ || die "bootstrap_post_import_hook failed"
+
+ # Remove any dangling symlink matching "*.m4" or "*.[ch]" in some
+ # gnulib-populated directories. Such .m4 files would cause aclocal to fail.
+ # The following requires GNU find 4.2.3 or newer. Considering the usual
+ # portability constraints of this script, that may seem a very demanding
+ # requirement, but it should be ok. Ignore any failure, which is fine,
+ # since this is only a convenience to help developers avoid the relatively
+ # unusual case in which a symlinked-to .m4 file is git-removed from gnulib
+ # between successive runs of this script.
+ find "$m4_base" "$source_base" \
+ -depth \( -name '*.m4' -o -name '*.[ch]' \) \
+ -type l -xtype l -delete > /dev/null 2>&1
+
+ # Invoke autoreconf with --force --install to ensure upgrades of tools
+ # such as ylwrap.
+ AUTORECONFFLAGS="--verbose --install --force $ACLOCAL_FLAGS"
+ AUTORECONFFLAGS="$AUTORECONFFLAGS --no-recursive"
+
+ # Tell autoreconf not to invoke autopoint or libtoolize; they were run above.
+ echo "running: AUTOPOINT=true LIBTOOLIZE=true $AUTORECONF $AUTORECONFFLAGS"
+ AUTOPOINT=true LIBTOOLIZE=true $AUTORECONF $AUTORECONFFLAGS \
+ || die "autoreconf failed"
+
+ # Get some extra files from gnulib, overriding existing files.
+ for file in $gnulib_extra_files; do
+ case $file in
+ */INSTALL) dst=INSTALL;;
+ build-aux/*) dst=$build_aux/${file#build-aux/};;
+ *) dst=$file;;
+ esac
+ symlink_to_dir "$GNULIB_SRCDIR" $file $dst \
+ || die "failed to symlink $file"
+ done
+
+ if test $with_gettext = yes; then
+ # Create gettext configuration.
+ echo "$0: Creating po/Makevars from po/Makevars.template ..."
+ rm -f po/Makevars
+ sed '
+ /^EXTRA_LOCALE_CATEGORIES *=/s/=.*/= '"$EXTRA_LOCALE_CATEGORIES"'/
+ /^COPYRIGHT_HOLDER *=/s/=.*/= '"$COPYRIGHT_HOLDER"'/
+ /^MSGID_BUGS_ADDRESS *=/s|=.*|= '"$MSGID_BUGS_ADDRESS"'|
+ /^XGETTEXT_OPTIONS *=/{
+ s/$/ \\/
+ a\
+ '"$XGETTEXT_OPTIONS"' $${end_of_xgettext_options+}
+ }
+ ' po/Makevars.template >po/Makevars \
+ || die 'cannot generate po/Makevars'
+
+ # If the 'gettext' module is in use, grab the latest Makefile.in.in.
+ # If only the 'gettext-h' module is in use, assume autopoint already
+ # put the correct version of this file into place.
+ case $gnulib_modules in
+ *gettext-h*) ;;
+ *gettext*)
+ cp $GNULIB_SRCDIR/build-aux/po/Makefile.in.in po/Makefile.in.in \
+ || die "cannot create po/Makefile.in.in"
+ ;;
+ esac
+
+ if test -d runtime-po; then
+ # Similarly for runtime-po/Makevars, but not quite the same.
+ rm -f runtime-po/Makevars
+ sed '
+ /^DOMAIN *=.*/s/=.*/= '"$package"'-runtime/
+ /^subdir *=.*/s/=.*/= runtime-po/
+ /^MSGID_BUGS_ADDRESS *=/s/=.*/= bug-'"$package"'@gnu.org/
+ /^XGETTEXT_OPTIONS *=/{
+ s/$/ \\/
+ a\
+ '"$XGETTEXT_OPTIONS_RUNTIME"' $${end_of_xgettext_options+}
+ }
+ ' po/Makevars.template >runtime-po/Makevars \
+ || die 'cannot generate runtime-po/Makevars'
+
+ # Copy identical files from po to runtime-po.
+ cp -p po/Makefile.in.in po/*-quot po/*.header po/*.sed po/*.sin runtime-po
+ fi
+ fi
+
+ bootstrap_epilogue
+
+ echo "$0: done. Now you can run './configure'."
+}
+
+# ----------------------------------------------------------------------------
+
+# Local Variables:
+# eval: (add-hook 'before-save-hook 'time-stamp nil t)
+# time-stamp-start: "scriptlibversion="
+# time-stamp-format: "%Y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
+
+usage() {
+ cat </dev/null && found_aux_dir=yes
-grep '^[ ]*AC_CONFIG_AUX_DIR('"$build_aux"')' configure.ac \
- >/dev/null && found_aux_dir=yes
-test $found_aux_dir = yes \
- || die "configure.ac lacks 'AC_CONFIG_AUX_DIR([$build_aux])'; add it"
+check_build_prerequisites $use_git
-# If $build_aux doesn't exist, create it now, otherwise some bits
-# below will malfunction. If creating it, also mark it as ignored.
-if test ! -d $build_aux; then
- mkdir $build_aux
- for dot_ig in x $vc_ignore; do
- test $dot_ig = x && continue
- insert_vc_ignore $dot_ig $build_aux
- done
-fi
-
-# Note this deviates from the version comparison in automake
-# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a
-# but this should suffice as we won't be specifying old
-# version formats or redundant trailing .0 in bootstrap.conf.
-# If we did want full compatibility then we should probably
-# use m4_version_compare from autoconf.
-sort_ver() { # sort -V is not generally available
- ver1="$1"
- ver2="$2"
-
- # split on '.' and compare each component
- i=1
- while : ; do
- p1=$(echo "$ver1" | cut -d. -f$i)
- p2=$(echo "$ver2" | cut -d. -f$i)
- if [ ! "$p1" ]; then
- echo "$1 $2"
- break
- elif [ ! "$p2" ]; then
- echo "$2 $1"
- break
- elif [ ! "$p1" = "$p2" ]; then
- if [ "$p1" -gt "$p2" ] 2>/dev/null; then # numeric comparison
- echo "$2 $1"
- elif [ "$p2" -gt "$p1" ] 2>/dev/null; then # numeric comparison
- echo "$1 $2"
- else # numeric, then lexicographic comparison
- lp=$(printf "$p1\n$p2\n" | LANG=C sort -n | tail -n1)
- if [ "$lp" = "$p2" ]; then
- echo "$1 $2"
- else
- echo "$2 $1"
- fi
- fi
- break
- fi
- i=$(($i+1))
- done
-}
-
-get_version_sed='
-# Move version to start of line.
-s/.*[v ]\([0-9]\)/\1/
-
-# Skip lines that do not start with version.
-/^[0-9]/!d
-
-# Remove characters after the version.
-s/[^.a-z0-9-].*//
-
-# The first component must be digits only.
-s/^\([0-9]*\)[a-z-].*/\1/
-
-#the following essentially does s/5.005/5.5/
-s/\.0*\([1-9]\)/.\1/g
-p
-q'
-
-get_version() {
- app=$1
-
- $app --version >/dev/null 2>&1 || { $app --version; return 1; }
-
- $app --version 2>&1 | sed -n "$get_version_sed"
-}
-
-check_versions() {
- ret=0
-
- while read app req_ver; do
- # We only need libtoolize from the libtool package.
- if test "$app" = libtool; then
- app=libtoolize
- fi
- # Exempt git if --no-git is in effect.
- if test "$app" = git; then
- $use_git || continue
- fi
- # Honor $APP variables ($TAR, $AUTOCONF, etc.)
- appvar=$(echo $app | LC_ALL=C tr '[a-z]-' '[A-Z]_')
- test "$appvar" = TAR && appvar=AMTAR
- case $appvar in
- GZIP) ;; # Do not use $GZIP: it contains gzip options.
- PERL::*) ;; # Keep perl modules as-is
- *) eval "app=\${$appvar-$app}" ;;
- esac
-
- # Handle the still-experimental Automake-NG programs specially.
- # They remain named as the mainstream Automake programs ("automake",
- # and "aclocal") to avoid gratuitous incompatibilities with
- # pre-existing usages (by, say, autoreconf, or custom autogen.sh
- # scripts), but correctly identify themselves (as being part of
- # "GNU automake-ng") when asked their version.
- case $app in
- automake-ng|aclocal-ng)
- app=${app%-ng}
- ($app --version | grep '(GNU automake-ng)') >/dev/null 2>&1 || {
- warn_ "Error: '$app' not found or not from Automake-NG"
- ret=1
- continue
- } ;;
- # Another check is for perl modules. These can be written as
- # e.g. perl::XML::XPath in case of XML::XPath module, etc.
- perl::*)
- # Extract module name
- app="${app#perl::}"
- if ! $PERL -m"$app" -e 'exit 0' >/dev/null 2>&1; then
- warn_ "Error: perl module '$app' not found"
- ret=1
- fi
- continue
- ;;
- esac
- if [ "$req_ver" = "-" ]; then
- # Merely require app to exist; not all prereq apps are well-behaved
- # so we have to rely on $? rather than get_version.
- if ! check_exists --verbose $app; then
- warn_ "Error: '$app' not found"
- ret=1
- fi
- else
- # Require app to produce a new enough version string.
- inst_ver=$(get_version $app)
- if [ ! "$inst_ver" ]; then
- warn_ "Error: '$app' not found"
- ret=1
- else
- latest_ver=$(sort_ver $req_ver $inst_ver | cut -d' ' -f2)
- if [ ! "$latest_ver" = "$inst_ver" ]; then
- warnf_ '%s\n' \
- "Error: '$app' version == $inst_ver is too old" \
- " '$app' version >= $req_ver is required"
- ret=1
- fi
- fi
- fi
- done
-
- return $ret
-}
-
-print_versions() {
- echo "Program Min_version"
- echo "----------------------"
- printf %s "$buildreq"
- echo "----------------------"
- # can't depend on column -t
-}
-
-# Find sha1sum, named gsha1sum on MacPorts, shasum on Mac OS X 10.6.
-# Also find the compatible sha1 utility on the BSDs
-if test x"$SKIP_PO" = x; then
- find_tool SHA1SUM sha1sum gsha1sum shasum sha1
-fi
-
-use_libtool=0
-# We'd like to use grep -E, to see if any of LT_INIT,
-# AC_PROG_LIBTOOL, AM_PROG_LIBTOOL is used in configure.ac,
-# but that's not portable enough (e.g., for Solaris).
-grep '^[ ]*A[CM]_PROG_LIBTOOL' configure.ac >/dev/null \
- && use_libtool=1
-grep '^[ ]*LT_INIT' configure.ac >/dev/null \
- && use_libtool=1
-if test $use_libtool = 1; then
- find_tool LIBTOOLIZE glibtoolize libtoolize
-fi
-
-# gnulib-tool requires at least automake and autoconf.
-# If either is not listed, add it (with minimum version) as a prerequisite.
-case $buildreq in
- *automake*) ;;
- *) buildreq="automake 1.9
-$buildreq" ;;
-esac
-case $buildreq in
- *autoconf*) ;;
- *) buildreq="autoconf 2.59
-$buildreq" ;;
-esac
-
-# When we can deduce that gnulib-tool will require patch,
-# and when patch is not already listed as a prerequisite, add it, too.
-if test -d "$local_gl_dir" \
- && ! find "$local_gl_dir" -name '*.diff' -exec false {} +; then
- case $buildreq in
- *patch*) ;;
- *) buildreq="patch -
-$buildreq" ;;
- esac
-fi
-
-if ! printf "$buildreq" | check_versions; then
- echo >&2
- if test -f README-prereq; then
- die "See README-prereq for how to get the prerequisite programs"
- else
- die "Please install the prerequisite programs"
- fi
-fi
-
-# Warn the user if autom4te appears to be broken; this causes known
-# issues with at least gettext 0.18.3.
-probe=$(echo 'm4_quote([hi])' | autom4te -l M4sugar -t 'm4_quote:$%' -)
-if test "x$probe" != xhi; then
- warn_ "WARNING: your autom4te wrapper eats stdin;"
- warn_ "if bootstrap fails, consider upgrading your autotools"
+if $bootstrap_sync; then
+ prepare_GNULIB_SRCDIR
+ upgrade_bootstrap "$@"
+ # Since we have now upgraded if needed, no need to try it a second time below.
+ bootstrap_sync=false
fi
echo "$0: Bootstrapping from checked-out $package sources..."
-# See if we can use gnulib's git-merge-changelog merge driver.
-if $use_git && test -d .git && check_exists git; then
- if git config merge.merge-changelog.driver >/dev/null ; then
- :
- elif check_exists git-merge-changelog; then
- echo "$0: initializing git-merge-changelog driver"
- git config merge.merge-changelog.name 'GNU-style ChangeLog merge driver'
- git config merge.merge-changelog.driver 'git-merge-changelog %O %A %B'
- else
- echo "$0: consider installing git-merge-changelog from gnulib"
- fi
+# Pass GNULIB_SRCDIR and GNULIB_REFDIR to any subsidiary commands that care.
+export GNULIB_SRCDIR
+export GNULIB_REFDIR
+
+if $pull && { $use_git || test -z "$SKIP_PO"; }; then
+ autopull \
+ `if $bootstrap_sync; then
+ echo ' --bootstrap-sync'
+ else
+ echo ' --no-bootstrap-sync'
+ fi` \
+ `if test -z "$checkout_only_file"; then echo ' --force'; fi` \
+ `if ! $use_git; then echo ' --no-git'; fi` \
+ `if test -n "$SKIP_PO"; then echo ' --skip-po'; fi` \
+ || die "could not fetch auxiliary files"
fi
-
-cleanup_gnulib() {
- status=$?
- rm -fr "$gnulib_path"
- exit $status
-}
-
-git_modules_config () {
- test -f .gitmodules && git config --file .gitmodules "$@"
-}
-
-if $use_gnulib; then
- if $use_git; then
- gnulib_path=$(git_modules_config submodule.gnulib.path)
- test -z "$gnulib_path" && gnulib_path=gnulib
- fi
-
- # Get gnulib files. Populate $GNULIB_SRCDIR, possibly updating a
- # submodule, for use in the rest of the script.
-
- case ${GNULIB_SRCDIR--} in
- -)
- # Note that $use_git is necessarily true in this case.
- if git_modules_config submodule.gnulib.url >/dev/null; then
- echo "$0: getting gnulib files..."
- git submodule init -- "$gnulib_path" || exit $?
- git submodule update -- "$gnulib_path" || exit $?
-
- elif [ ! -d "$gnulib_path" ]; then
- echo "$0: getting gnulib files..."
-
- trap cleanup_gnulib 1 2 13 15
-
- shallow=
- if test -z "$GNULIB_REVISION"; then
- git clone -h 2>&1 | grep -- --depth > /dev/null && shallow='--depth 2'
- git clone $shallow ${GNULIB_URL:-$default_gnulib_url} "$gnulib_path" \
- || cleanup_gnulib
- else
- git fetch -h 2>&1 | grep -- --depth > /dev/null && shallow='--depth 2'
- mkdir -p "$gnulib_path"
- # Only want a shallow checkout of $GNULIB_REVISION, but git does not
- # support cloning by commit hash. So attempt a shallow fetch by commit
- # hash to minimize the amount of data downloaded and changes needed to
- # be processed, which can drastically reduce download and processing
- # time for checkout. If the fetch by commit fails, a shallow fetch can
- # not be performed because we do not know what the depth of the commit
- # is without fetching all commits. So fallback to fetching all commits.
- git -C "$gnulib_path" init
- git -C "$gnulib_path" remote add origin ${GNULIB_URL:-$default_gnulib_url}
- git -C "$gnulib_path" fetch $shallow origin "$GNULIB_REVISION" \
- || git -C "$gnulib_path" fetch origin \
- || cleanup_gnulib
- git -C "$gnulib_path" reset --hard FETCH_HEAD
- fi
-
- trap - 1 2 13 15
- fi
- GNULIB_SRCDIR=$gnulib_path
- ;;
- *)
- # Use GNULIB_SRCDIR directly or as a reference.
- if $use_git && test -d "$GNULIB_SRCDIR"/.git && \
- git_modules_config submodule.gnulib.url >/dev/null; then
- echo "$0: getting gnulib files..."
- if git submodule -h|grep -- --reference > /dev/null; then
- # Prefer the one-liner available in git 1.6.4 or newer.
- git submodule update --init --reference "$GNULIB_SRCDIR" \
- "$gnulib_path" || exit $?
- else
- # This fallback allows at least git 1.5.5.
- if test -f "$gnulib_path"/gnulib-tool; then
- # Since file already exists, assume submodule init already complete.
- git submodule update -- "$gnulib_path" || exit $?
- else
- # Older git can't clone into an empty directory.
- rmdir "$gnulib_path" 2>/dev/null
- git clone --reference "$GNULIB_SRCDIR" \
- "$(git_modules_config submodule.gnulib.url)" "$gnulib_path" \
- && git submodule init -- "$gnulib_path" \
- && git submodule update -- "$gnulib_path" \
- || exit $?
- fi
- fi
- GNULIB_SRCDIR=$gnulib_path
- fi
- ;;
- esac
-
- if test -d "$GNULIB_SRCDIR"/.git && test -n "$GNULIB_REVISION" \
- && ! git_modules_config submodule.gnulib.url >/dev/null; then
- (cd "$GNULIB_SRCDIR" && git checkout "$GNULIB_REVISION") || cleanup_gnulib
- fi
-
- # $GNULIB_SRCDIR now points to the version of gnulib to use, and
- # we no longer need to use git or $gnulib_path below here.
-
- if $bootstrap_sync; then
- cmp -s "$0" "$GNULIB_SRCDIR/build-aux/bootstrap" || {
- echo "$0: updating bootstrap and restarting..."
- case $(sh -c 'echo "$1"' -- a) in
- a) ignored=--;;
- *) ignored=ignored;;
- esac
- exec sh -c \
- 'cp "$1" "$2" && shift && exec "${CONFIG_SHELL-/bin/sh}" "$@"' \
- $ignored "$GNULIB_SRCDIR/build-aux/bootstrap" \
- "$0" "$@" --no-bootstrap-sync
- }
- fi
-
- gnulib_tool=$GNULIB_SRCDIR/gnulib-tool
- <$gnulib_tool || exit $?
+if $gen; then
+ autogen \
+ `if $copy; then echo ' --copy'; fi` \
+ `if test -z "$checkout_only_file"; then echo ' --force'; fi` \
+ || die "could not generate auxiliary files"
fi
-# Get translations.
-
-download_po_files() {
- subdir=$1
- domain=$2
- echo "$me: getting translations into $subdir for $domain..."
- cmd=$(printf "$po_download_command_format" "$subdir" "$domain")
- eval "$cmd"
-}
-
-# Mirror .po files to $po_dir/.reference and copy only the new
-# or modified ones into $po_dir. Also update $po_dir/LINGUAS.
-# Note po files that exist locally only are left in $po_dir but will
-# not be included in LINGUAS and hence will not be distributed.
-update_po_files() {
- # Directory containing primary .po files.
- # Overwrite them only when we're sure a .po file is new.
- po_dir=$1
- domain=$2
-
- # Mirror *.po files into this dir.
- # Usually contains *.s1 checksum files.
- ref_po_dir="$po_dir/.reference"
-
- test -d $ref_po_dir || mkdir $ref_po_dir || return
- download_po_files $ref_po_dir $domain \
- && ls "$ref_po_dir"/*.po 2>/dev/null |
- sed 's|.*/||; s|\.po$||' > "$po_dir/LINGUAS" || return
-
- langs=$(cd $ref_po_dir && echo *.po | sed 's/\.po//g')
- test "$langs" = '*' && langs=x
- for po in $langs; do
- case $po in x) continue;; esac
- new_po="$ref_po_dir/$po.po"
- cksum_file="$ref_po_dir/$po.s1"
- if ! test -f "$cksum_file" ||
- ! test -f "$po_dir/$po.po" ||
- ! $SHA1SUM -c "$cksum_file" < "$new_po" > /dev/null 2>&1; then
- echo "$me: updated $po_dir/$po.po..."
- cp "$new_po" "$po_dir/$po.po" \
- && $SHA1SUM < "$new_po" > "$cksum_file" || return
- fi
- done
-}
-
-case $SKIP_PO in
-'')
- if test -d po; then
- update_po_files po $package || exit
- fi
-
- if test -d runtime-po; then
- update_po_files runtime-po $package-runtime || exit
- fi;;
-esac
-
-version_controlled_file() {
- parent=$1
- file=$2
- if test -d .git; then
- git rm -n "$file" > /dev/null 2>&1
- elif test -d .svn; then
- svn log -r HEAD "$file" > /dev/null 2>&1
- elif test -d CVS; then
- grep -F "/${file##*/}/" "$parent/CVS/Entries" 2>/dev/null |
- grep '^/[^/]*/[0-9]' > /dev/null
- else
- warn_ "no version control for $file?"
- false
- fi
-}
-
-# NOTE: we have to be careful to run both autopoint and libtoolize
-# before gnulib-tool, since gnulib-tool is likely to provide newer
-# versions of files "installed" by these two programs.
-# Then, *after* gnulib-tool (see below), we have to be careful to
-# run autoreconf in such a way that it does not run either of these
-# two just-pre-run programs.
-
-# Import from gettext.
-with_gettext=yes
-grep '^[ ]*AM_GNU_GETTEXT_VERSION(' configure.ac >/dev/null || \
- with_gettext=no
-
-if test $with_gettext = yes || test $use_libtool = 1; then
-
- tempbase=.bootstrap$$
- trap "rm -f $tempbase.0 $tempbase.1" 1 2 13 15
-
- > $tempbase.0 > $tempbase.1 &&
- find . ! -type d -print | sort > $tempbase.0 || exit
-
- if test $with_gettext = yes; then
- # Released autopoint has the tendency to install macros that have been
- # obsoleted in current gnulib, so run this before gnulib-tool.
- echo "$0: $AUTOPOINT --force"
- $AUTOPOINT --force || exit
- fi
-
- # Autoreconf runs aclocal before libtoolize, which causes spurious
- # warnings if the initial aclocal is confused by the libtoolized
- # (or worse out-of-date) macro directory.
- # libtoolize 1.9b added the --install option; but we support back
- # to libtoolize 1.5.22, where the install action was default.
- if test $use_libtool = 1; then
- install=
- case $($LIBTOOLIZE --help) in
- *--install*) install=--install ;;
- esac
- echo "running: $LIBTOOLIZE $install --copy"
- $LIBTOOLIZE $install --copy
- fi
-
- find . ! -type d -print | sort >$tempbase.1
- old_IFS=$IFS
- IFS=$nl
- for file in $(comm -13 $tempbase.0 $tempbase.1); do
- IFS=$old_IFS
- parent=${file%/*}
- version_controlled_file "$parent" "$file" || {
- for dot_ig in x $vc_ignore; do
- test $dot_ig = x && continue
- ig=$parent/$dot_ig
- insert_vc_ignore "$ig" "${file##*/}"
- done
- }
- done
- IFS=$old_IFS
-
- rm -f $tempbase.0 $tempbase.1
- trap - 1 2 13 15
-fi
-
-# Import from gnulib.
-
-if $use_gnulib; then
- gnulib_tool_options="\
- --no-changelog\
- --aux-dir=$build_aux\
- --doc-base=$doc_base\
- --lib=$gnulib_name\
- --m4-base=$m4_base/\
- --source-base=$source_base/\
- --tests-base=$tests_base\
- --local-dir=$local_gl_dir\
- $gnulib_tool_option_extras\
- "
- if test $use_libtool = 1; then
- case "$gnulib_tool_options " in
- *' --libtool '*) ;;
- *) gnulib_tool_options="$gnulib_tool_options --libtool" ;;
- esac
- fi
- echo "$0: $gnulib_tool $gnulib_tool_options --import ..."
- $gnulib_tool $gnulib_tool_options --import $gnulib_modules \
- || die "gnulib-tool failed"
-
- for file in $gnulib_files; do
- symlink_to_dir "$GNULIB_SRCDIR" $file \
- || die "failed to symlink $file"
- done
-fi
-
-bootstrap_post_import_hook \
- || die "bootstrap_post_import_hook failed"
-
-# Don't proceed if there are uninitialized submodules. In particular,
-# the next step will remove dangling links, which might be links into
-# uninitialized submodules.
-#
-# Uninitialized submodules are listed with an initial dash.
-if $use_git && git submodule | grep '^-' >/dev/null; then
- die "some git submodules are not initialized. " \
- "Run 'git submodule update --init' and bootstrap again."
-fi
-
-# Remove any dangling symlink matching "*.m4" or "*.[ch]" in some
-# gnulib-populated directories. Such .m4 files would cause aclocal to fail.
-# The following requires GNU find 4.2.3 or newer. Considering the usual
-# portability constraints of this script, that may seem a very demanding
-# requirement, but it should be ok. Ignore any failure, which is fine,
-# since this is only a convenience to help developers avoid the relatively
-# unusual case in which a symlinked-to .m4 file is git-removed from gnulib
-# between successive runs of this script.
-find "$m4_base" "$source_base" \
- -depth \( -name '*.m4' -o -name '*.[ch]' \) \
- -type l -xtype l -delete > /dev/null 2>&1
-
-# Invoke autoreconf with --force --install to ensure upgrades of tools
-# such as ylwrap.
-AUTORECONFFLAGS="--verbose --install --force -I $m4_base $ACLOCAL_FLAGS"
-
-# Some systems (RHEL 5) are using ancient autotools, for which the
-# --no-recursive option had not been invented. Detect that lack and
-# omit the option when it's not supported. FIXME in 2017: remove this
-# hack when RHEL 5 autotools are updated, or when they become irrelevant.
-case $($AUTORECONF --help) in
- *--no-recursive*) AUTORECONFFLAGS="$AUTORECONFFLAGS --no-recursive";;
-esac
-
-# Tell autoreconf not to invoke autopoint or libtoolize; they were run above.
-echo "running: AUTOPOINT=true LIBTOOLIZE=true $AUTORECONF $AUTORECONFFLAGS"
-AUTOPOINT=true LIBTOOLIZE=true $AUTORECONF $AUTORECONFFLAGS \
- || die "autoreconf failed"
-
-# Get some extra files from gnulib, overriding existing files.
-for file in $gnulib_extra_files; do
- case $file in
- */INSTALL) dst=INSTALL;;
- build-aux/*) dst=$build_aux/${file#build-aux/};;
- *) dst=$file;;
- esac
- symlink_to_dir "$GNULIB_SRCDIR" $file $dst \
- || die "failed to symlink $file"
-done
-
-if test $with_gettext = yes; then
- # Create gettext configuration.
- echo "$0: Creating po/Makevars from po/Makevars.template ..."
- rm -f po/Makevars
- sed '
- /^EXTRA_LOCALE_CATEGORIES *=/s/=.*/= '"$EXTRA_LOCALE_CATEGORIES"'/
- /^COPYRIGHT_HOLDER *=/s/=.*/= '"$COPYRIGHT_HOLDER"'/
- /^MSGID_BUGS_ADDRESS *=/s|=.*|= '"$MSGID_BUGS_ADDRESS"'|
- /^XGETTEXT_OPTIONS *=/{
- s/$/ \\/
- a\
- '"$XGETTEXT_OPTIONS"' $${end_of_xgettext_options+}
- }
- ' po/Makevars.template >po/Makevars \
- || die 'cannot generate po/Makevars'
-
- # If the 'gettext' module is in use, grab the latest Makefile.in.in.
- # If only the 'gettext-h' module is in use, assume autopoint already
- # put the correct version of this file into place.
- case $gnulib_modules in
- *gettext-h*) ;;
- *gettext*)
- cp $GNULIB_SRCDIR/build-aux/po/Makefile.in.in po/Makefile.in.in \
- || die "cannot create po/Makefile.in.in"
- ;;
- esac
-
- if test -d runtime-po; then
- # Similarly for runtime-po/Makevars, but not quite the same.
- rm -f runtime-po/Makevars
- sed '
- /^DOMAIN *=.*/s/=.*/= '"$package"'-runtime/
- /^subdir *=.*/s/=.*/= runtime-po/
- /^MSGID_BUGS_ADDRESS *=/s/=.*/= bug-'"$package"'@gnu.org/
- /^XGETTEXT_OPTIONS *=/{
- s/$/ \\/
- a\
- '"$XGETTEXT_OPTIONS_RUNTIME"' $${end_of_xgettext_options+}
- }
- ' po/Makevars.template >runtime-po/Makevars \
- || die 'cannot generate runtime-po/Makevars'
-
- # Copy identical files from po to runtime-po.
- (cd po && cp -p Makefile.in.in *-quot *.header *.sed *.sin ../runtime-po)
- fi
-fi
-
-bootstrap_epilogue
-
-echo "$0: done. Now you can run './configure'."
+# ----------------------------------------------------------------------------
# Local Variables:
-# eval: (add-hook 'before-save-hook 'time-stamp)
+# eval: (add-hook 'before-save-hook 'time-stamp nil t)
# time-stamp-start: "scriptversion="
-# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-format: "%Y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:
diff --git a/bootstrap.conf b/bootstrap.conf
index 3590aba99..40e0b0cf4 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -24,6 +24,7 @@ gnulib_modules="
argp
base64
error
+ filevercmp
fnmatch
getdelim
getline
@@ -62,8 +63,6 @@ checkout_only_file=
copy=true
vc_ignore=
-SKIP_PO=t
-
# Build prerequisites
buildreq="\
autoconf 2.64
@@ -107,4 +106,23 @@ bootstrap_post_import_hook () {
bootstrap_epilogue () {
mv INSTALL.grub INSTALL
+
+ if [ "x$SKIP_PO" = "x" ]; then
+ # Generate LINGUAS with all supported languages. Bootstrap will
+ # generate a LINGUAS, but it will not contain the autogenerated
+ # languages.
+ autogenerated="en@quot en@hebrew de@hebrew en@cyrillic en@greek en@arabic en@piglatin de_CH"
+
+ {
+ # NOTE: xargs has no POSIX compliant way to avoid running the program
+ # given as an argument when there are no input lines. So ensure that
+ # basename is always run with at least one argument, the empty string,
+ # and ignore the first line of output.
+ ls po/*.po | xargs -L 100 basename -s .po -a "" | tail -n +2
+ for x in $autogenerated; do
+ rm -f "po/$x.po"
+ echo "$x"
+ done
+ } | sort | uniq | xargs >po/LINGUAS
+ fi
}
diff --git a/conf/Makefile.common b/conf/Makefile.common
index 4d38ff034..7ef171b2b 100644
--- a/conf/Makefile.common
+++ b/conf/Makefile.common
@@ -24,6 +24,8 @@ if COND_HAVE_PCI
CFLAGS_PLATFORM += -DGRUB_HAS_PCI
endif
+CPPFLAGS_GCRY_ASM = @CPPFLAGS_GCRY_ASM@
+
# Other options
CPPFLAGS_DEFAULT = -DGRUB_FILE=\"$(subst $(srcdir)/,,$<)\"
diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist
index f4e3c5fba..d22b6c862 100644
--- a/conf/Makefile.extra-dist
+++ b/conf/Makefile.extra-dist
@@ -34,23 +34,63 @@ EXTRA_DIST += grub-core/lib/gnulib-patches/fix-regexec-resource-leak.patch
EXTRA_DIST += grub-core/lib/gnulib-patches/fix-gcc-15-compile.patch
EXTRA_DIST += grub-core/lib/gnulib-patches/fix-unused-value.patch
+EXTRA_DIST += grub-core/lib/libgcrypt-patches/01_md.patch
+EXTRA_DIST += grub-core/lib/libgcrypt-patches/02_keccak_sse.patch
+EXTRA_DIST += grub-core/lib/libgcrypt-patches/03_mpiutil_alloc.patch
+EXTRA_DIST += grub-core/lib/libgcrypt-patches/03_sexp_free.patch
+EXTRA_DIST += grub-core/lib/libgcrypt-patches/04_aria.patch
+EXTRA_DIST += grub-core/lib/libgcrypt-patches/05_disable_rsa_shake.patch
+EXTRA_DIST += grub-core/lib/libgcrypt-patches/06_blake.patch
+EXTRA_DIST += grub-core/lib/libgcrypt-patches/07_disable_64div.patch
+EXTRA_DIST += grub-core/lib/libgcrypt-patches/08_sexp_leak.patch
+EXTRA_DIST += grub-core/lib/libgcrypt-patches/09-blake2b-hash-buffers.patch
+EXTRA_DIST += grub-core/lib/libgcrypt-patches/10-kdf-use-GPG-errs.patch
+EXTRA_DIST += grub-core/lib/libgcrypt-patches/11-kdf-remove-unsupported-kdfs.patch
+EXTRA_DIST += grub-core/lib/libgcrypt-patches/12-kdf-use-grub_divmod64.patch
+EXTRA_DIST += grub-core/lib/libgcrypt-patches/13_add_hwfeatures.patch
+EXTRA_DIST += grub-core/lib/libgcrypt-patches/14_fix_build_shaext.patch
+EXTRA_DIST += grub-core/lib/libgcrypt-patches/15_build_sha256_x86_64_efi_opt_code.patch
+EXTRA_DIST += grub-core/lib/libgcrypt-patches/16_build_sha512_x86_64_efi_opt_code.patch
+
+EXTRA_DIST += grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch
+EXTRA_DIST += grub-core/lib/libtasn1-patches/0002-libtasn1-replace-strcat-with-strcpy-in-_asn1_str_cat.patch
+EXTRA_DIST += grub-core/lib/libtasn1-patches/0003-libtasn1-replace-strcat-with-_asn1_str_cat.patch
+EXTRA_DIST += grub-core/lib/libtasn1-patches/0004-libtasn1-adjust-the-header-paths-in-libtasn1.h.patch
+EXTRA_DIST += grub-core/lib/libtasn1-patches/0005-libtasn1-Use-grub_divmod64-for-division.patch
+EXTRA_DIST += grub-core/lib/libtasn1-patches/0006-libtasn1-fix-the-potential-buffer-overrun.patch
+EXTRA_DIST += grub-core/lib/libtasn1-patches/0007-asn1_test-include-asn1_test.h-only.patch
+EXTRA_DIST += grub-core/lib/libtasn1-patches/0008-asn1_test-rename-the-main-functions-to-the-test-name.patch
+EXTRA_DIST += grub-core/lib/libtasn1-patches/0009-asn1_test-return-either-0-or-1-to-reflect-the-result.patch
+EXTRA_DIST += grub-core/lib/libtasn1-patches/0010-asn1_test-remove-verbose-and-the-unnecessary-printf.patch
+EXTRA_DIST += grub-core/lib/libtasn1-patches/0011-asn1_test-print-the-error-messages-with-grub_printf.patch
+EXTRA_DIST += grub-core/lib/libtasn1-patches/0012-asn1_test-use-the-grub-specific-functions-and-types.patch
+EXTRA_DIST += grub-core/lib/libtasn1-patches/0013-asn1_test-enable-the-testcase-only-when-GRUB_LONG_MA.patch
+
EXTRA_DIST += grub-core/lib/libgcrypt
EXTRA_DIST += grub-core/lib/libgcrypt-grub/mpi/generic
+EXTRA_DIST += grub-core/lib/libtasn1
EXTRA_DIST += $(shell find $(top_srcdir)/include -name '*.h')
EXTRA_DIST += $(shell find $(top_srcdir)/grub-core/lib -name '*.h')
EXTRA_DIST += grub-core/efiemu/runtime/config.h
+EXTRA_DIST += grub-core/tests/crypto_cipher_mode_vectors.h
EXTRA_DIST += grub-core/tests/asn1/asn1_test.h
EXTRA_DIST += $(shell find $(top_srcdir)/grub-core/tests/asn1/tests -name '*.h')
EXTRA_DIST += $(shell find $(top_srcdir)/grub-core/commands/tpm2_key_protector -name '*.h')
+EXTRA_DIST += grub-core/commands/tpm2_key_protector/tpm2key.asn
+
+EXTRA_DIST += grub-core/commands/appendedsig/appendedsig.h
EXTRA_DIST += grub-core/lib/LzmaDec.c
EXTRA_DIST += grub-core/fs/cpio_common.c
EXTRA_DIST += BUGS
+EXTRA_DIST += MAINTAINERS
+EXTRA_DIST += SECURITY
EXTRA_DIST += util/i386/efi/grub-dumpdevtree
EXTRA_DIST += util/spkmodem-recv.c
EXTRA_DIST += util/import_gcrypth.sed
+EXTRA_DIST += util/import_gcrypt_inth.sed
EXTRA_DIST += util/bin2h.c
EXTRA_DIST += util/grub-gen-asciih.c
EXTRA_DIST += util/grub-gen-widthspec.c
@@ -132,6 +172,9 @@ EXTRA_DIST += po/hebrew.sed
EXTRA_DIST += tests/dfly-mbr-mbexample.mbr.img.gz
EXTRA_DIST += tests/dfly-mbr-mbexample.dfly.img.gz
+EXTRA_DIST += tests/iso9660_ce_loop2.iso.gz
+EXTRA_DIST += tests/iso9660_ce_loop.iso.gz
+EXTRA_DIST += tests/iso9660_early_ce.iso.gz
EXTRA_DIST += coreboot.cfg
@@ -142,6 +185,8 @@ EXTRA_DIST += tests/file_filter/file.lzop
EXTRA_DIST += tests/file_filter/file.lzop.sig
EXTRA_DIST += tests/file_filter/file.xz
EXTRA_DIST += tests/file_filter/file.xz.sig
+EXTRA_DIST += tests/file_filter/file.zstd
+EXTRA_DIST += tests/file_filter/file.zstd.sig
EXTRA_DIST += tests/file_filter/keys
EXTRA_DIST += tests/file_filter/keys.pub
EXTRA_DIST += tests/file_filter/test.cfg
diff --git a/configure.ac b/configure.ac
index 83e3ddf90..d8ca1b7c1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -34,7 +34,7 @@ dnl "TARGET_" (such as TARGET_CC, TARGET_CFLAGS, etc.) are used for
dnl the target type. See INSTALL for full list of variables and
dnl description of the relationships between them.
-AC_INIT([GRUB],[2.13],[bug-grub@gnu.org])
+AC_INIT([GRUB],[2.15],[bug-grub@gnu.org])
AS_CASE(["$ERROR_PLATFORM_NOT_SUPPORT_SSP"],
[n | no | nO | N | No | NO], [ERROR_PLATFORM_NOT_SUPPORT_SSP=no],
@@ -53,7 +53,7 @@ save_program_prefix="${program_prefix}"
AC_CANONICAL_TARGET
program_prefix="${save_program_prefix}"
-AM_INIT_AUTOMAKE([1.11])
+AM_INIT_AUTOMAKE([1.11 tar-ustar])
AC_PREREQ(2.64)
AC_CONFIG_SRCDIR([include/grub/dl.h])
AC_CONFIG_HEADERS([config-util.h])
@@ -1148,6 +1148,239 @@ if test "x$grub_cv_cc_fno_ident" = xyes; then
TARGET_CFLAGS="$TARGET_CFLAGS -fno-ident"
fi
+# Implementation of the --disable-amd64-as-feature-detection switch.
+AC_MSG_CHECKING([whether to enable AMD64 as(1) feature detection])
+if test x$target_cpu = xx86_64 -a x$platform = xefi; then
+ CPPFLAGS_GCRY_ASM="-D__x86_64 -DHAVE_CPU_ARCH_X86"
+ AC_ARG_ENABLE(amd64-as-feature-detection,
+ AS_HELP_STRING([--disable-amd64-as-feature-detection],
+ [Disable the auto-detection of AMD64 as(1) features]),
+ [amd64_as_feature_detection=$enableval],
+ [amd64_as_feature_detection=yes])
+else
+ CPPFLAGS_GCRY_ASM=
+ amd64_as_feature_detection=no
+fi
+AC_MSG_RESULT($amd64_as_feature_detection)
+
+#
+# Check whether GCC assembler supports features needed for libgcrypt amd64
+# implementations
+#
+if test $amd64_as_feature_detection = yes; then
+ AC_CACHE_CHECK([whether GCC assembler is compatible for amd64 assembly implementations],
+ [grub_cv_gcc_x86_platform_as_ok],
+ [if test "$target_cpu" != "x86_64" ; then
+ grub_cv_gcc_x86_platform_as_ok="n/a"
+ else
+ grub_cv_gcc_x86_platform_as_ok=no
+ AC_LINK_IFELSE([AC_LANG_PROGRAM(
+ [[__asm__(
+ /* Test if '.type' and '.size' are supported. */
+ /* These work only on ELF targets. */
+ ".text\n\t"
+ "asmfunc:\n\t"
+ ".size asmfunc,.-asmfunc;\n\t"
+ ".type asmfunc,@function;\n\t"
+ /* Test if assembler allows use of '/' for constant division
+ * (Solaris/x86 issue). If previous constant division check
+ * and "-Wa,--divide" workaround failed, this causes assembly
+ * to be disable on this machine. */
+ "xorl \$(123456789/12345678), %ebp;\n\t"
+ );
+ void asmfunc(void);]], [ asmfunc(); ])],
+ [grub_cv_gcc_x86_platform_as_ok=yes])
+ fi])
+ if test "$grub_cv_gcc_x86_platform_as_ok" = "yes"; then
+ # Define __PIC__ to ensure the assembly code use PIC instructions
+ CPPFLAGS_GCRY_ASM="$CPPFLAGS_GCRY_ASM -D__PIC__=1 -DHAVE_COMPATIBLE_GCC_AMD64_PLATFORM_AS"
+ fi
+
+ #
+ # Check whether GCC assembler supports Intel syntax
+ #
+ AC_CACHE_CHECK([whether GCC assembler is compatible for Intel syntax assembly implementations],
+ [grub_cv_gcc_platform_as_ok_for_intel_syntax],
+ [if test "$target_cpu" != "x86_64" ; then
+ grub_cv_gcc_platform_as_ok_for_intel_syntax="n/a"
+ else
+ grub_cv_gcc_platform_as_ok_for_intel_syntax=no
+ AC_LINK_IFELSE([AC_LANG_PROGRAM(
+ [[__asm__(
+ ".intel_syntax noprefix\n\t"
+ ".text\n\t"
+ "actest:\n\t"
+ "pxor xmm1, xmm7;\n\t"
+ "vperm2i128 ymm2, ymm3, ymm0, 1;\n\t"
+ "add eax, ebp;\n\t"
+ "rorx eax, ebp, 1;\n\t"
+ "sub eax, [esp + 4];\n\t"
+ "add dword ptr [esp + eax], 0b10101;\n\t"
+ ".att_syntax prefix\n\t"
+ );
+ void actest(void);]], [ actest(); ])],
+ [grub_cv_gcc_platform_as_ok_for_intel_syntax=yes])
+ fi])
+ if test "$grub_cv_gcc_platform_as_ok_for_intel_syntax" = "yes" ; then
+ CPPFLAGS_GCRY_ASM="$CPPFLAGS_GCRY_ASM -DHAVE_INTEL_SYNTAX_PLATFORM_AS"
+ fi
+
+ #
+ # Check whether GCC inline assembler supports SSSE3 instructions
+ #
+ AC_CACHE_CHECK([whether GCC inline assembler supports SSSE3 instructions],
+ [grub_cv_gcc_inline_asm_ssse3],
+ [if test "$target_cpu" != "x86_64" ; then
+ grub_cv_gcc_inline_asm_ssse3="n/a"
+ else
+ grub_cv_gcc_inline_asm_ssse3=no
+ AC_LINK_IFELSE([AC_LANG_PROGRAM(
+ [[static unsigned char be_mask[16] __attribute__ ((aligned (16))) =
+ { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
+ void a(void) {
+ __asm__("pshufb %[mask], %%xmm2\n\t"::[mask]"m"(*be_mask):);
+ }]], [ a(); ] )],
+ [grub_cv_gcc_inline_asm_ssse3=yes])
+ fi])
+ if test "$grub_cv_gcc_inline_asm_ssse3" = "yes" ; then
+ CPPFLAGS_GCRY_ASM="$CPPFLAGS_GCRY_ASM -DHAVE_GCC_INLINE_ASM_SSSE3"
+ fi
+
+ #
+ # Check whether GCC inline assembler supports SHA Extensions instructions.
+ #
+ AC_CACHE_CHECK([whether GCC inline assembler supports SHA Extensions instructions],
+ [grub_cv_gcc_inline_asm_shaext],
+ [if test "$target_cpu" != "x86_64" ; then
+ grub_cv_gcc_inline_asm_shaext="n/a"
+ else
+ grub_cv_gcc_inline_asm_shaext=no
+ AC_LINK_IFELSE([AC_LANG_PROGRAM(
+ [[void a(void) {
+ __asm__("sha1rnds4 \$0, %%xmm1, %%xmm3\n\t":::"cc");
+ __asm__("sha1nexte %%xmm1, %%xmm3\n\t":::"cc");
+ __asm__("sha1msg1 %%xmm1, %%xmm3\n\t":::"cc");
+ __asm__("sha1msg2 %%xmm1, %%xmm3\n\t":::"cc");
+ __asm__("sha256rnds2 %%xmm0, %%xmm1, %%xmm3\n\t":::"cc");
+ __asm__("sha256msg1 %%xmm1, %%xmm3\n\t":::"cc");
+ __asm__("sha256msg2 %%xmm1, %%xmm3\n\t":::"cc");
+ }]], [ a(); ] )],
+ [grub_cv_gcc_inline_asm_shaext=yes])
+ fi])
+ if test "$grub_cv_gcc_inline_asm_shaext" = "yes" ; then
+ CPPFLAGS_GCRY_ASM="$CPPFLAGS_GCRY_ASM -DHAVE_GCC_INLINE_ASM_SHAEXT -DENABLE_SHAEXT_SUPPORT"
+ fi
+
+ #
+ # Check whether GCC inline assembler supports SSE4.1 instructions.
+ #
+ AC_CACHE_CHECK([whether GCC inline assembler supports SSE4.1 instructions],
+ [grub_cv_gcc_inline_asm_sse41],
+ [if test "$target_cpu" != "x86_64" ; then
+ grub_cv_gcc_inline_asm_sse41="n/a"
+ else
+ grub_cv_gcc_inline_asm_sse41=no
+ AC_LINK_IFELSE([AC_LANG_PROGRAM(
+ [[void a(void) {
+ int i;
+ __asm__("pextrd \$2, %%xmm0, %[out]\n\t" : [out] "=m" (i));
+ }]], [ a(); ] )],
+ [grub_cv_gcc_inline_asm_sse41=yes])
+ fi])
+ if test "$grub_cv_gcc_inline_asm_sse41" = "yes" ; then
+ CPPFLAGS_GCRY_ASM="$CPPFLAGS_GCRY_ASM -DHAVE_GCC_INLINE_ASM_SSE41"
+ fi
+
+ #
+ # Check whether GCC inline assembler supports AVX instructions
+ #
+ AC_CACHE_CHECK([whether GCC inline assembler supports AVX instructions],
+ [grub_cv_gcc_inline_asm_avx],
+ [if test "$target_cpu" != "x86_64" ; then
+ grub_cv_gcc_inline_asm_avx="n/a"
+ else
+ grub_cv_gcc_inline_asm_avx=no
+ AC_LINK_IFELSE([AC_LANG_PROGRAM(
+ [[void a(void) {
+ __asm__("xgetbv; vaesdeclast (%[mem]),%%xmm0,%%xmm7\n\t"::[mem]"r"(0):);
+ }]], [ a(); ] )],
+ [grub_cv_gcc_inline_asm_avx=yes])
+ fi])
+ if test "$grub_cv_gcc_inline_asm_avx" = "yes" ; then
+ CPPFLAGS_GCRY_ASM="$CPPFLAGS_GCRY_ASM -DHAVE_GCC_INLINE_ASM_AVX"
+ fi
+
+ #
+ # Check whether GCC inline assembler supports AVX2 instructions
+ #
+ AC_CACHE_CHECK([whether GCC inline assembler supports AVX2 instructions],
+ [grub_cv_gcc_inline_asm_avx2],
+ [if test "$target_cpu" != "x86_64" ; then
+ grub_cv_gcc_inline_asm_avx2="n/a"
+ else
+ grub_cv_gcc_inline_asm_avx2=no
+ AC_LINK_IFELSE([AC_LANG_PROGRAM(
+ [[void a(void) {
+ __asm__("xgetbv; vpbroadcastb %%xmm7,%%ymm1\n\t":::"cc");
+ }]], [ a(); ] )],
+ [grub_cv_gcc_inline_asm_avx2=yes])
+ fi])
+ if test "$grub_cv_gcc_inline_asm_avx2" = "yes" ; then
+ CPPFLAGS_GCRY_ASM="$CPPFLAGS_GCRY_ASM -DHAVE_GCC_INLINE_ASM_AVX2"
+ fi
+
+ #
+ # Check whether GCC inline assembler supports AVX512 instructions
+ #
+ AC_CACHE_CHECK([whether GCC inline assembler supports AVX512 instructions],
+ [grub_cv_gcc_inline_asm_avx512],
+ [if test "$target_cpu" != "x86_64" ; then
+ grub_cv_gcc_inline_asm_avx512="n/a"
+ else
+ grub_cv_gcc_inline_asm_avx512=no
+ AC_LINK_IFELSE([AC_LANG_PROGRAM(
+ [[void a(void) {
+ __asm__("xgetbv; vpopcntq %%zmm7, %%zmm1%{%%k1%}%{z%};\n\t":::"cc");
+ __asm__("vpexpandb %%zmm3, %%zmm1;\n\t":::"cc");
+ __asm__("vpxorq %%xmm7, %%xmm7, %%xmm7;\n\t":::"cc");
+ __asm__("vpxorq %%ymm7, %%ymm7, %%ymm7;\n\t":::"cc");
+ __asm__("vpxorq (%%eax)%{1to8%}, %%zmm7, %%zmm7;\n\t":::"cc");
+ }]], [ a(); ] )],
+ [grub_cv_gcc_inline_asm_avx512=yes])
+ fi])
+ if test "$grub_cv_gcc_inline_asm_avx512" = "yes" ; then
+ CPPFLAGS_GCRY_ASM="$CPPFLAGS_GCRY_ASM -DHAVE_GCC_INLINE_ASM_AVX512"
+ fi
+
+ #
+ # Check whether GCC inline assembler supports BMI2 instructions
+ #
+ AC_CACHE_CHECK([whether GCC inline assembler supports BMI2 instructions],
+ [grub_cv_gcc_inline_asm_bmi2],
+ [if test "$target_cpu" != "x86_64" ; then
+ grub_cv_gcc_inline_asm_bmi2="n/a"
+ else
+ grub_cv_gcc_inline_asm_bmi2=no
+ AC_LINK_IFELSE([AC_LANG_PROGRAM(
+ [[unsigned int a(unsigned int x, unsigned int y) {
+ unsigned int tmp1, tmp2;
+ asm ("rorxl %2, %1, %0"
+ : "=r" (tmp1)
+ : "rm0" (x), "J" (32 - ((23) & 31)));
+ asm ("andnl %2, %1, %0"
+ : "=r" (tmp2)
+ : "r0" (x), "rm" (y));
+ return tmp1 + tmp2;
+ }]], [ a(1, 2); ] )],
+ [grub_cv_gcc_inline_asm_bmi2=yes])
+ fi])
+ if test "$grub_cv_gcc_inline_asm_bmi2" = "yes" ; then
+ CPPFLAGS_GCRY_ASM="$CPPFLAGS_GCRY_ASM -DHAVE_GCC_INLINE_ASM_BMI2"
+ fi
+fi
+
+AC_SUBST(CPPFLAGS_GCRY_ASM)
+
CFLAGS="$TARGET_CFLAGS"
@@ -1228,7 +1461,6 @@ elif test x$grub_cv_target_cc_link_format = x-mi386pe || test x$grub_cv_target_c
TARGET_IMG_LDSCRIPT='$(top_srcdir)'"/conf/i386-cygwin-img-ld.sc"
TARGET_IMG_LDFLAGS="-Wl,-T${TARGET_IMG_LDSCRIPT}"
TARGET_IMG_LDFLAGS_AC="-Wl,-T${srcdir}/conf/i386-cygwin-img-ld.sc"
- TARGET_IMG_BASE_LDOPT="-Wl,-Ttext"
TARGET_IMG_CFLAGS=
else
TARGET_APPLE_LINKER=0
@@ -1236,7 +1468,6 @@ else
TARGET_IMG_LDSCRIPT=
TARGET_IMG_LDFLAGS='-Wl,-N'
TARGET_IMG_LDFLAGS_AC='-Wl,-N'
- TARGET_IMG_BASE_LDOPT="-Wl,-Ttext"
TARGET_IMG_CFLAGS=
fi
@@ -1312,21 +1543,6 @@ AC_SUBST(TARGET_LDFLAGS_OLDMAGIC)
LDFLAGS="$TARGET_LDFLAGS"
-if test "$target_cpu" = x86_64 || test "$target_cpu" = sparc64 || test "$target_cpu" = riscv64 ; then
- # Use large model to support 4G memory
- AC_CACHE_CHECK([whether option -mcmodel=large works], grub_cv_cc_mcmodel, [
- CFLAGS="$TARGET_CFLAGS -mcmodel=large"
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],
- [grub_cv_cc_mcmodel=yes],
- [grub_cv_cc_mcmodel=no])
- ])
- if test "x$grub_cv_cc_mcmodel" = xyes; then
- TARGET_CFLAGS="$TARGET_CFLAGS -mcmodel=large"
- elif test "$target_cpu" = sparc64 || test "$target_cpu" = riscv64; then
- TARGET_CFLAGS="$TARGET_CFLAGS -mcmodel=medany"
- fi
-fi
-
if test "$target_cpu"-"$platform" = x86_64-efi; then
# EFI writes to stack below %rsp, we must not use the red zone
AC_CACHE_CHECK([whether option -mno-red-zone works], grub_cv_cc_no_red_zone, [
@@ -1435,6 +1651,21 @@ fi]
CFLAGS="$TARGET_CFLAGS"
+if test "$target_cpu" = x86_64 || test "$target_cpu" = sparc64 || test "$target_cpu" = riscv64 ; then
+ # Use large model to support 4G memory
+ AC_CACHE_CHECK([whether option -mcmodel=large works], grub_cv_cc_mcmodel, [
+ CFLAGS="$TARGET_CFLAGS -mcmodel=large"
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],
+ [grub_cv_cc_mcmodel=yes],
+ [grub_cv_cc_mcmodel=no])
+ ])
+ if test "x$grub_cv_cc_mcmodel" = xyes; then
+ TARGET_CFLAGS="$TARGET_CFLAGS -mcmodel=large"
+ elif test "$target_cpu" = sparc64 || test "$target_cpu" = riscv64; then
+ TARGET_CFLAGS="$TARGET_CFLAGS -mcmodel=medany"
+ fi
+fi
+
# Stack smashing protector.
grub_CHECK_STACK_PROTECTOR
AC_ARG_ENABLE([stack-protector],
@@ -1561,7 +1792,24 @@ LIBS=""
# Defined in acinclude.m4.
grub_ASM_USCORE
grub_PROG_TARGET_CC
+
+# The error message produced by autoconf if autoconf-archive is not installed is
+# quite misleading and not very helpful. So, try point people in the right direction.
+m4_ifndef([AX_CHECK_LINK_FLAG], [m4_fatal([autoconf-archive is missing. You must install it to generate the configure script.])])
+
if test "x$TARGET_APPLE_LINKER" != x1 ; then
+AX_CHECK_LINK_FLAG([-Wl,--image-base,0x400000],
+ [TARGET_IMG_BASE_LDOPT="-Wl,--image-base"],
+ [TARGET_IMG_BASE_LDOPT="-Wl,-Ttext"],
+ [],
+ [AC_LANG_SOURCE([
+asm (".globl start; start:");
+asm (".globl _start; _start:");
+asm (".globl __start; __start:");
+void __main (void);
+void __main (void) {}
+int main (void);
+ ])])
grub_PROG_OBJCOPY_ABSOLUTE
fi
grub_PROG_LD_BUILD_ID_NONE
diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
index 62ad43c9d..51a0923ec 100644
--- a/docs/grub-dev.texi
+++ b/docs/grub-dev.texi
@@ -77,7 +77,7 @@ This edition documents version @value{VERSION}.
* Coding style::
* Finding your way around::
* Contributing Changes::
-* Setting up and running test suite::
+* Tests::
* Updating External Code::
* Debugging::
* Porting::
@@ -485,8 +485,17 @@ If your intention is to just get started, please do not submit a inclusion
request. Instead, please subscribe to the mailing list, and communicate first
(e.g. sending a patch, asking a question, commenting on another message...).
+@node Tests
+@chapter Tests
+
+@menu
+* Setting up and running test suite::
+* Writing new tests::
+@end menu
+
+
@node Setting up and running test suite
-@chapter Setting up and running test suite
+@section Setting up and running test suite
GRUB is basically a tiny operating system with read support for many file
systems and which has been ported to a variety of architectures. As such, its
@@ -495,6 +504,47 @@ These dependencies are currently documented in the
@uref{https://git.savannah.gnu.org/cgit/grub.git/tree/INSTALL, INSTALL}
file in the source repository. Once installed, the test suite can be started
by running the @command{make check} command from the GRUB build directory.
+To properly run all the tests, the test suite must be run as the privileged
+user, for instance to run the filesystem tests, which require mounting
+filesystem images. Of course, virtualization or containers may be used, but
+this requires extra configuration outside the scope of this document.
+
+@node Writing new tests
+@section Writing new tests
+
+There are two kinds of tests in the GRUB test suite: native and non-native.
+Native tests are those which run on the build architecture and non-native
+run on the target architecture. The non-native tests are run in a QEMU
+virtual machine using the grub-shell script in tests/util. When writing a
+new test, first determine if it should run on the host or the target and
+then look at the existing tests as examples of how to they should be written.
+
+The GRUB test suite uses automake (@uref{https://www.gnu.org/software/automake/manual/automake.html#Tests, see documention here}). One thing of importance
+to note is that tests have 4 classes of return codes: SKIP (77), HARD ERROR
+(99), PASS (0), and FAIL (all other codes). A
+@uref{https://www.gnu.org/software/automake/manual/automake.html#index-test-skip, SKIP return code}
+should be returned when this test cannot be performed and thus should be
+skipped. Typically this is because the target does not support the test,
+such as the ohci USB test for the powerpc-ieee1275 target because there
+are no native drivers for that target. A
+@uref{https://www.gnu.org/software/automake/manual/automake.html#index-Distinction-between-errors-and-failures-in-testsuites, HARD ERROR return code}
+should be returned when a failure in something other than what is intended
+to be tested happens. This is commonly returned when the test cannot be
+properly run because of deficiencies in the test environment, eg. when
+testing the xfs filesystem, but the kernel has no support for mounting xfs
+volumes. A SKIP should never be returned for a HARD ERROR condition
+because at best the person running the test does not know if the test was
+skipped because it doesn't apply to the target or because the tester failed
+to setup the environment properly. At worst, the tester will believe that
+everything is okay without realizing that the tests are not covering all
+the code that it should.
+
+Keep portability in mind while creating a new tests so that the tests can be
+run on a variety of systems and shells. Do not use bashisms. Also try to avoid
+using utilities that would unecessarily add software dependencies. Sometimes
+this is unavoidable. Copy or adapt existing test implementations where feasible.
+If the test is native and requires root, make sure to check that the test is
+run with the root user and return HARD ERROR if it is not.
@node Updating External Code
@chapter Updating external code
@@ -900,26 +950,26 @@ is to be run in a GDB session running with the @file{gdb_grub} GDB script.
@node Porting
@chapter Porting
-GRUB2 is designed to be easily portable accross platforms. But because of the
+GRUB2 is designed to be easily portable across platforms. But because of the
nature of bootloader every new port must be done separately. Here is how I did
-MIPS (loongson and ARC) and Xen ports. Note than this is more of suggestions,
-not absolute truth.
+MIPS (loongson and ARC) and Xen ports. Note that this is more of a suggestion,
+and not absolute truth.
-First of all grab any architecture specifications you can find in public
-(please avoid NDA).
+First of all, grab any architecture specifications you can find in the public
+domain (please avoid NDA).
First stage is ``Hello world''. I've done it outside of GRUB for simplicity.
Your task is to have a small program which is loadable as bootloader and
-clearly shows its presence to you. If you have easily accessible console
-you can just print a message. If you have a mapped framebuffer you know address
+clearly shows its presence to you. If you have an easily accessible console
+you can just print a message. If you have a mapped framebuffer you know the address
of, you can draw a square. If you have a debug facility, just hanging without
-crashing might be enough. For the first stage you can choose to load the
-bootloader across the network since format for network image is often easier
+crashing might be enough. For the first stage, you can choose to load the
+bootloader across the network since the format for a network image is often easier
than for local boot and it skips the need of small intermediary stages and
-nvram handling. Additionally you can often have a good idea of the needed
-format by running ``file'' on any netbootable executable for given platform.
+nvram handling. Additionally, you can often have a good idea of the needed
+format by running ``file'' on any netbootable executable for the given platform.
-This program should probably have 2 parts: an assembler and C one. Assembler one
+This program should probably have 2 parts: an assembler and C one. The assembler one
handles BSS cleaning and other needed setup (on some platforms you may need
to switch modes or copy the executable to its definitive position). So your code
may look like (x86 assembly for illustration purposes)
@@ -961,12 +1011,12 @@ Sometimes you need a third file: assembly stubs for ABI-compatibility.
Once this file is functional it's time to move it into GRUB2. The startup
assembly file goes to grub-core/kern/$cpu/$platform/startup.S. You should also
-include grub/symbol.h and replace call to entry point with call to
+include grub/symbol.h and replace the call to entry point with call to
EXT_C(grub_main). The C file goes to grub-core/kern/$cpu/$platform/init.c
and its entry point is renamed to void grub_machine_init (void). Keep final
-infinite loop for now. Stubs file if any goes to
+infinite loop for now. Stub files if any goes to
grub-core/kern/$cpu/$platform/callwrap.S. Sometimes either $cpu or $platform
-is dropped if file is used on several cpus respectivelyplatforms.
+is dropped if file is used on several cpus respectively or platforms.
Check those locations if they already have what you're looking for.
Then modify in configure.ac the following parts:
@@ -984,10 +1034,10 @@ esac
@end example
Sometimes CPU have additional architecture names which don't influence booting.
-You might want to have some canonical name to avoid having bunch of identical
+You might want to have some canonical name to avoid having a bunch of identical
platforms with different names.
-NOTE: it doesn't influence compile optimisations which depend solely on
+NOTE: It doesn't influence compile optimisations which depend solely on
chosen compiler and compile options.
@example
@@ -1049,7 +1099,7 @@ AM_CONDITIONAL([COND_powerpc_ieee1275], [test x$target_cpu = xpowerpc -a x$platf
@end example
Next stop is gentpl.py. You need to add your platform to the list of supported
-ones (sorry that this list is duplicated):
+ones (unfortunately, this list is duplicated):
@example
GRUB_PLATFORMS = [ "emu", "i386_pc", "i386_efi", "i386_qemu", "i386_coreboot",
@@ -1059,21 +1109,21 @@ GRUB_PLATFORMS = [ "emu", "i386_pc", "i386_efi", "i386_qemu", "i386_coreboot",
"mips_qemu_mips", "s390_mainframe" ]
@end example
-You may also want already to add new platform to one or several of available
+Now you may also want to add a new platform to one or several of available
groups. In particular we always have a group for each CPU even when only
one platform for given CPU is available.
Then comes grub-core/Makefile.core.def. In the block ``kernel'' you'll need
to define ldflags for your platform ($cpu_$platform_ldflags). You also need to
-declare startup asm file ($cpu_$platform_startup) as well as any other files
-(e.g. init.c and callwrap.S) (e.g. $cpu_$platform = kern/$cpu/$platform/init.c).
-At this stage you will also need to add dummy dl.c and cache.S with functions
+declare a startup asm file ($cpu_$platform_startup) as well as any other files,
+like init.c or callwrap.S (e.g. $cpu_$platform = kern/$cpu/$platform/init.c).
+At this stage, you will also need to add a dummy dl.c and cache.S with functions
grub_err_t grub_arch_dl_check_header (void *ehdr), grub_err_t
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) (dl.c) and
void grub_arch_sync_caches (void *address, grub_size_t len) (cache.S). They
won't be used for now.
-You will need to create directory include/$cpu/$platform and a file
+You will need to create a directory include/$cpu/$platform and a file
include/$cpu/types.h. The latter following this template:
@example
@@ -1097,7 +1147,7 @@ include/$cpu/types.h. The latter following this template:
You will also need to add a dummy file to datetime and setjmp modules to
avoid any of it having no files. It can be just completely empty at this stage.
-You'll need to make grub-mkimage.c (util/grub_mkimage.c) aware of the needed
+You'll need to make grub-mkimage (in util/grub_mkimage.c) aware of the needed
format. For most commonly used formats like ELF, PE, aout or raw the support
is already present and you'll need to make it follow the existant code paths
for your platform adding adjustments if necessary. When done compile:
@@ -1108,47 +1158,47 @@ for your platform adding adjustments if necessary. When done compile:
make > /dev/null
@end example
-And create image
+And create an image:
@example
./grub-mkimage -d grub-core -O $format_id -o test.img
@end example
-And it's time to test your test.img.
+And now it's time to test your test.img.
-If it works next stage is to have heap, console and timer.
+If it works, the next stage is to have a heap, console and timer.
To have the heap working you need to determine which regions are suitable for
-heap usage, allocate them from firmware and map (if applicable). Then call
-grub_mm_init_region (void *start, grub_size_t s) for every of this region.
-As a shortcut for early port you can allocate right after _end or have
-a big static array for heap. If you do you'll probably need to come back to
+heap usage, allocate them from the firmware and map them, if applicable.
+Then call grub_mm_init_region (void *start, grub_size_t s) for each region.
+As a shortcut for an early port, you can allocate right after _end or have
+a big static array for heap. If you do, you'll probably need to come back to
this later. As for output console you should distinguish between an array of
-text, terminfo or graphics-based console. Many of real-world examples don't
+text, terminfo or graphics-based console. Many real-world examples don't
fit perfectly into any of these categories but one of the models is easier
-to be used as base. In second and third case you should add your platform to
+to be used as base. In the second and third case, you should add your platform to
terminfokernel respectively videoinkernel group. A good example of array of
-text is i386-pc (kern/i386/pc/init.c and term/i386/pc/console.c).
-Of terminfo is ieee1275 (kern/ieee1275/init.c and term/ieee1275/console.c).
-Of video is loongson (kern/mips/loongson/init.c). Note that terminfo has
+text is i386-pc (kern/i386/pc/init.c and term/i386/pc/console.c),
+of terminfo is ieee1275 (kern/ieee1275/init.c and term/ieee1275/console.c),
+and of video is loongson (kern/mips/loongson/init.c). Note that terminfo has
to be inited in 2 stages: one before (to get at least rudimentary console
as early as possible) and another after the heap (to get full-featured console).
-For the input there are string of keys, terminfo and direct hardware. For string
-of keys look at i386-pc (same files), for terminfo ieee1275 (same files) and for
-hardware loongson (kern/mips/loongson/init.c and term/at_keyboard.c).
+For the input, there is a string of keys (eg. i386-pc, same files), terminfo
+(ieee1275, same files), and hardware (loongson, see kern/mips/loongson/init.c
+and term/at_keyboard.c).
-For the timer you'll need to call grub_install_get_time_ms (...) with as sole
-argument a function returning a grub_uint64_t of a number of milliseconds
+For the timer, you'll need to call grub_install_get_time_ms (...) with the
+sole argument a function returning a grub_uint64_t number of milliseconds
elapsed since arbitrary point in the past.
-Once these steps accomplished you can remove the inifinite loop and you should
+Once these steps are accomplished, you can remove the inifinite loop and you should
be able to get to the minimal console. Next step is to have module loading
-working. For this you'll need to fill kern/$cpu/dl.c and kern/$cpu/cache.S
+working. For this, you'll need to fill kern/$cpu/dl.c and kern/$cpu/cache.S
with real handling of relocations and respectively the real sync of I and D
caches. Also you'll need to decide where in the image to store the modules.
Usual way is to have it concatenated at the end. In this case you'll need to
modify startup.S to copy modules out of bss to let's say ALIGN_UP (_end, 8)
-before cleaning out bss. You'll probably find useful to add total_module_size
+before cleaning out bss. You'll probably find it useful to add total_module_size
field to startup.S. In init.c you need to set grub_modbase to the address
where modules can be found. You may need grub_modules_get_end () to avoid
declaring the space occupied by modules as usable for heap. You can test modules
@@ -1164,9 +1214,9 @@ Once this works, you should think of implementing disk access. Look around
disk/ for examples.
Then, very importantly, you probably need to implement the actual loader
-(examples available in loader/)
+(examples available in loader/).
-Last step to have minimally usable port is to add support to grub-install to
+The last step to have a minimally usable port is to add support to grub-install to
put GRUB in a place where firmware or platform will pick it up.
Next steps are: filling datetime.c, setjmp.S, network (net/drivers),
@@ -1175,15 +1225,15 @@ video (video/), halt (lib/), reboot (lib/).
Please add your platform to Platform limitations and Supported kernels chapter
in user documentation and mention any steps you skipped which result in reduced
features or performance. Here is the quick checklist of features. Some of them
-are less important than others and skipping them is completely ok, just needs
+are less important than others and skipping them is completely ok, it just needs
to be mentioned in user documentation.
Checklist:
@itemize
-@item Is heap big enough?
-@item Which charset is supported by console?
-@item Does platform have disk driver?
-@item Do you have network card support?
+@item Is the heap big enough?
+@item Which charset is supported by the console?
+@item Does the platform have disk driver?
+@item Is there network card support?
@item Are you able to retrieve datetime (with date)?
@item Are you able to set datetime (with date)?
@item Is serial supported?
@@ -1209,16 +1259,16 @@ GRUB?
@node Error Handling
@chapter Error Handling
-Error handling in GRUB 2 is based on exception handling model. As C language
+Error handling in GRUB 2 is based on exception handling model. As the C language
doesn't directly support exceptions, exception handling behavior is emulated
-in software.
+in software.
-When exception is raised, function must return to calling function. If calling
+When an exception is raised, the function must return to the calling function. If the calling
function does not provide handling of the exception it must return back to its
-calling function and so on, until exception is handled. If exception is not
-handled before prompt is displayed, error message will be shown to user.
+calling function and so on, until the exception is handled. If the exception is not
+handled before a prompt is displayed, error message will be shown to user.
-Exception information is stored on @code{grub_errno} global variable. If
+Exception information is stored in the @code{grub_errno} global variable. If
@code{grub_errno} variable contains value @code{GRUB_ERR_NONE}, there is no active
exception and application can continue normal processing. When @code{grub_errno} has
other value, it is required that application code either handles this error or
diff --git a/docs/grub.texi b/docs/grub.texi
index 34b3484dc..c948e1ee7 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -1642,6 +1642,13 @@ This option is unset by default, and is deprecated in favour of the less
confusing @samp{GRUB_TIMEOUT_STYLE=countdown} or
@samp{GRUB_TIMEOUT_STYLE=hidden}.
+@item GRUB_FORCE_EFI_ALL_VIDEO
+When set to true, this will allow grub-mkconfig to generate a GRUB config
+that supports loading the all_video module on the EFI platform instead of
+just the efi_gop and efi_uga modules.
+
+This option is unset by default.
+
@end table
For more detailed customisation of @command{grub-mkconfig}'s output, you may
@@ -2943,7 +2950,7 @@ Heavily limited platforms:
Lightly limited platforms:
@itemize
-@item *-xen: limited only by adress space and RAM size.
+@item *-xen: limited only by address space and RAM size.
@item i386-qemu: kernel.img (.text + .data + .bss) is limited by 392704 bytes.
(core.img would be limited by ROM size but it's unlimited on qemu
@item All EFI platforms: limited by contiguous RAM size and possibly firmware bugs
@@ -3271,6 +3278,8 @@ GRUB. Others may be used freely in GRUB configuration files.
@menu
* Special environment variables::
* Environment block::
+* Special environment block variables::
+* Passing environment variables through Xen::
@end menu
@@ -3280,7 +3289,10 @@ GRUB. Others may be used freely in GRUB configuration files.
These variables have special meaning to GRUB.
@menu
+* appendedsig_key_mgmt::
* biosnum::
+* blsuki_save_default::
+* check_appended_signatures::
* check_signatures::
* chosen::
* cmdpath::
@@ -3331,6 +3343,19 @@ These variables have special meaning to GRUB.
@end menu
+@node appendedsig_key_mgmt
+@subsection appendedsig_key_mgmt
+
+This variable controls whether GRUB enforces appended signature validation
+using either @code{static} or @code{dynamic} key management. It is automatically
+set by GRUB to either @code{static} or @code{dynamic} based on the
+@strong{'ibm,secure-boot'} device tree property and Platform KeyStore (PKS).
+Also, it can be explicitly set to either @code{static} or @code{dynamic} by
+setting the @code{appendedsig_key_mgmt} variable from the GRUB console
+when the GRUB is not locked down.
+
+@xref{Using appended signatures} for more information.
+
@node biosnum
@subsection biosnum
@@ -3344,11 +3369,30 @@ For an alternative approach which also changes BIOS drive mappings for the
chain-loaded system, @pxref{drivemap}.
+@node blsuki_save_default
+@subsection blsuki_save_default
+
+If this variable is set, menu entries generated from BLS config files
+(@pxref{blscfg}) or UKI files (@pxref{uki}) will be set as the default boot
+entry when selected.
+
+@node check_appended_signatures
+@subsection check_appended_signatures
+
+This variable controls whether GRUB enforces appended signature validation on
+loaded kernel and GRUB module files. It is automatically set by GRUB
+to either @code{no} or @code{yes} based on the @strong{'ibm,secure-boot'} device
+tree property. Also, it can be explicitly set to either @code{no} or @code{yes} by
+setting the @code{check_appended_signatures} variable from the GRUB console
+when the GRUB is not locked down.
+
+@xref{Using appended signatures} for more information.
+
@node check_signatures
@subsection check_signatures
-This variable controls whether GRUB enforces digital signature
-validation on loaded files. @xref{Using digital signatures}.
+This variable controls whether GRUB enforces GPG-style digital signature
+validation on loaded files. @xref{Using GPG-style digital signatures}.
@node chosen
@subsection chosen
@@ -3868,9 +3912,119 @@ For safety reasons, this storage is only available when installed on a plain
disk (no LVM or RAID), using a non-checksumming filesystem (no ZFS), and
using BIOS or EFI functions (no ATA, USB or IEEE1275).
+On Btrfs filesystems, a reserved area in the filesystem header may be used to
+store the environment block. This static block avoids the problems of updating
+a normal file on a copy-on-write filesystem, where writing raw block is not
+stable and requires metadata update. The reserved area provides a fixed
+location that GRUB can update directly, allowing commands such as
+@command{grub-reboot} and @samp{GRUB_SAVEDEFAULT} to function correctly on
+Btrfs volumes.
+
@command{grub-mkconfig} uses this facility to implement
@samp{GRUB_SAVEDEFAULT} (@pxref{Simple configuration}).
+
+@node Special environment block variables
+@section Special environment block variables
+
+These special variables are usually written to the environment block
+(@pxref{Environment block}) to customize the behavior of @file{grub.cfg}
+generated by @command{grub-mkconfig}.
+
+@menu
+* saved_entry::
+* next_entry::
+* env_block::
+@end menu
+
+
+@node saved_entry
+@subsection saved_entry
+
+The @var{saved_entry} variable sets the default boot entry in @file{grub.cfg}
+created by @command{grub-mkconfig}. It can be set with
+@command{grub-set-default} to choose a default entry, or at runtime with the
+@code{savedefault} function in grub.cfg to save the current entry as the new
+default. This may require write access by GRUB.
+
+
+@node next_entry
+@subsection next_entry
+
+The @var{next_entry} variable sets the boot entry for the next boot only. After
+it is used, GRUB clears the value so it is not reused. This requires write
+access to the environment block (@pxref{Environment block}) at runtime. The
+@command{grub-reboot} command is usually used instead of changing this variable
+directly.
+
+
+@node env_block
+@subsection env_block
+
+If the filesystem is Btrfs and the disk is not an abstracted device such as
+LVM, RAID, or encryption, the reserved space in the Btrfs header can be used as
+the environment block (@pxref{Environment block}). This provides a fixed raw
+block that GRUB can reliably write to. The @var{env_block} records this
+location in GRUB blocklist syntax (@pxref{Block list syntax}) so that
+@command{grub-editenv} and @file{grub.cfg} know how to access and use the
+external raw block.
+
+This variable is initialized when @file{grubenv} is first created by
+@command{grub-editenv} and is treated as read-only to avoid being overwritten
+with an unpredictable value.
+
+
+@node Passing environment variables through Xen
+@section Passing environment variables through Xen
+
+If you are using a GRUB image as the kernel for a PV or PVH Xen virtual
+machine, you can pass environment variables from Xen's dom0 to the VM through
+the Xen-provided kernel command line. When combined with a properly configured
+guest, this can be used to customize the guest's behavior on bootup via the
+VM's Xen configuration file.
+
+GRUB will parse the kernel command line passed to it by Xen during bootup.
+The command line will be split into space-delimited words. Single and
+double quotes may be used to quote words or portions of words that contain
+spaces. Single quotes will be considered part of a word if inside double
+quotes, and vice versa. Arbitrary characters may be backslash-escaped to make
+them a literal component of a word rather than being parsed as quotes or word
+separators. The command line must consist entirely of printable 7-bit ASCII
+characters and spaces. If a non-printing ASCII character is found anywhere in
+the command line, the entire command line will be ignored by GRUB. (This
+splitter algorithm is meant to behave somewhat like Bash's word splitting.)
+
+Each word should be a variable assignment in the format ``variable'' or
+``variable=value''. Variable names must contain only the characters A-Z, a-z,
+and underscore (``_''). Variable names must begin with the string
+``xen_grub_env_''. Variable values can contain arbitrary printable 7-bit
+ASCII characters and space. If any variable contains an illegal name, that
+variable will be ignored.
+
+If a variable name and value are both specified, the variable will be set to
+the specified value. If only a variable name is specified, the variable's
+value will be set to ``1''.
+
+The following is a simple example of how to use this functionality to append
+arbitrary variables to a guest's kernel command line:
+
+@example
+# In the Xen configuration file for the guest
+name = "linux_vm"
+type = "pvh"
+kernel = "/path/to/grub-i386-xen_pvh.bin"
+extra = "xen_grub_env_linux_append='loglevel=3'"
+memory = 1024
+disk = [ "file:/srv/vms/linux_vm.img,sda,w" ]
+
+# In the guest's GRUB configuration file
+menuentry "Linux VM with dom0-specified kernel parameters" @{
+ search --set=root --label linux_vm --hint hd0,msdos1
+ linux /boot/vmlinuz root=LABEL=linux_vm $@{xen_grub_env_linux_append@}
+ initrd /boot/initrd.img
+@}
+@end example
+
@node Modules
@chapter Modules
@@ -3889,6 +4043,8 @@ Modules can be loaded via the @command{insmod} (@pxref{insmod}) command.
* aout_module::
* appleldr_module::
* archelp_module::
+* argon2_module::
+* argon2_test_module::
* at_keyboard_module::
* ata_module::
* backtrace_module::
@@ -3922,6 +4078,7 @@ Modules can be loaded via the @command{insmod} (@pxref{insmod}) command.
* cpio_be_module::
* cpuid_module::
* crc64_module::
+* crypto_cipher_mode_test_module::
* crypto_module::
* cryptodisk_module::
* cs5536_module::
@@ -3935,6 +4092,7 @@ Modules can be loaded via the @command{insmod} (@pxref{insmod}) command.
* div_test_module::
* dm_nv_module::
* drivemap_module::
+* dsa_sexp_test_module::
* echo_module::
* efi_gop_module::
* efi_uga_module::
@@ -3963,24 +4121,33 @@ Modules can be loaded via the @command{insmod} (@pxref{insmod}) command.
* fshelp_module::
* functional_test_module::
* gcry_arcfour_module::
+* gcry_aria_module::
+* gcry_blake2_module::
* gcry_blowfish_module::
* gcry_camellia_module::
* gcry_cast5_module::
* gcry_crc_module::
* gcry_des_module::
* gcry_dsa_module::
+* gcry_gost28147_module::
+* gcry_gostr3411_94_module::
* gcry_idea_module::
+* gcry_keccak_module::
* gcry_md4_module::
* gcry_md5_module::
* gcry_rfc2268_module::
* gcry_rijndael_module::
* gcry_rmd160_module::
* gcry_rsa_module::
+* gcry_salsa20_module::
* gcry_seed_module::
* gcry_serpent_module::
* gcry_sha1_module::
* gcry_sha256_module::
* gcry_sha512_module::
+* gcry_sm3_module::
+* gcry_sm4_module::
+* gcry_stribog_module::
* gcry_tiger_module::
* gcry_twofish_module::
* gcry_whirlpool_module::
@@ -4099,6 +4266,7 @@ Modules can be loaded via the @command{insmod} (@pxref{insmod}) command.
* probe_module::
* procfs_module::
* progress_module::
+* pubkey_module::
* pxe_module::
* pxechain_module::
* raid5rec_module::
@@ -4111,6 +4279,7 @@ Modules can be loaded via the @command{insmod} (@pxref{insmod}) command.
* reiserfs_module::
* relocator_module::
* romfs_module::
+* rsa_sexp_test_module::
* scsi_module::
* sdl_module::
* search_module::
@@ -4244,6 +4413,15 @@ computer (Intel based Macs).
This module provides Archive Helper functions for archive based file systems
such as TAR and CPIO archives.
+@node argon2_module
+@section argon2
+This module provides support for the Argon2 key derivation function.
+
+@node argon2_test_module
+@section argon2_test
+This module is intended for performing a functional test of the Argon2
+operation in GRUB.
+
@node at_keyboard_module
@section at_keyboard
This module provides support for the AT keyboard input for the GRUB terminal.
@@ -4433,6 +4611,10 @@ various CPU features. @xref{cpuid} for more information.
@section crc64
This module provides support for the CRC64 operation.
+@node crypto_cipher_mode_test_module
+@section crypto_cipher_mode_test
+This module performs various cipher mode encryption/decryption tests
+
@node crypto_module
@section crypto
This module provides library support for various base cryptography operations
@@ -4496,6 +4678,10 @@ This module provides support for handling some Nvidia "fakeraid" disk devices.
This module provides support for the @command{drivemap} to manage BIOS drive
mappings. @xref{drivemap} for more information.
+@node dsa_sexp_test_module
+@section dsa_sexp_test
+This module provides a test of the libgcrypt DSA functionality in GRUB.
+
@node echo_module
@section echo
This module provides support for the @command{echo} to display a line of text.
@@ -4640,6 +4826,16 @@ If security is a concern, RC4 / arcfour cipher is consider broken (multiple
known vulnerabilities make this insecure).
This GRUB module is based on libgcrypt.
+@node gcry_aria_module
+@section gcry_aria
+This module provides support for the ARIA cipher.
+This GRUB module is based on libgcrypt.
+
+@node gcry_blake2_module
+@section gcry_blake2
+This module provides support for the BLAKE2b and BLAKE2s message digests.
+This GRUB module is based on libgcrypt.
+
@node gcry_blowfish_module
@section gcry_blowfish
This module provides support for the Blowfish cipher.
@@ -4674,12 +4870,28 @@ This GRUB module is based on libgcrypt.
This module provides support for the Digital Signature Algorithm (DSA) cipher.
This GRUB module is based on libgcrypt.
+@node gcry_gost28147_module
+@section gcry_gost28147
+This module provides support for the GOST 28147-89 cipher.
+This GRUB module is based on libgcrypt.
+
+@node gcry_gostr3411_94_module
+@section gcry_gostr3411_94
+This module provides support for the GOST R 34.11-94 message digest.
+This GRUB module is based on libgcrypt.
+
@node gcry_idea_module
@section gcry_idea
This module provides support for the International Data Encryption Algorithm
(IDEA) cipher.
This GRUB module is based on libgcrypt.
+@node gcry_keccak_module
+@section gcry_keccak
+This module provides support for the SHA3 hash message digests (including
+SHAKE128 and SHAKE256).
+This GRUB module is based on libgcrypt.
+
@node gcry_md4_module
@section gcry_md4
This module provides support for the Message Digest 4 (MD4) message digest.
@@ -4714,6 +4926,11 @@ This GRUB module is based on libgcrypt.
This module provides support for the Rivest–Shamir–Adleman (RSA) cipher.
This GRUB module is based on libgcrypt.
+@node gcry_salsa20_module
+@section gcry_salsa20
+This module provides support for the Salsa20 cipher.
+This GRUB module is based on libgcrypt.
+
@node gcry_seed_module
@section gcry_seed
This module provides support for the SEED cipher.
@@ -4744,6 +4961,21 @@ This module provides support for the Secure Hash Algorithm 2 (384 and 512 bit)
(SHA-384 / SHA-512) message digests.
This GRUB module is based on libgcrypt.
+@node gcry_sm3_module
+@section gcry_sm3
+This module provides support for the SM3 message digest.
+This GRUB module is based on libgcrypt.
+
+@node gcry_sm4_module
+@section gcry_sm4
+This module provides support for the SM4 cipher.
+This GRUB module is based on libgcrypt.
+
+@node gcry_stribog_module
+@section gcry_stribog
+This module provides support for the GOST R 34.11-2012 (Stribog) message digest.
+This GRUB module is based on libgcrypt.
+
@node gcry_tiger_module
@section gcry_tiger
This module provides support for the Tiger, Tiger 1, and Tiger 2 message
@@ -5132,7 +5364,8 @@ enforced (@pxref{Lockdown}).
@node memtools_module
@section memtools
This module provides support for GRUB development / debugging commands
-@command{lsmem}, @command{lsfreemem}, and @command{stress_big_allocs}.
+@command{lsmem}, @command{lsfreemem}, @command{lsmemregions}, and
+@command{stress_big_allocs}.
@node minicmd_module
@section minicmd
@@ -5509,6 +5742,11 @@ like interface to some GRUB internal data.
@section progress
This module provides support for showing file loading progress to the terminal.
+@node pubkey_module
+@section pubkey
+This module provides supporting functions for using RSA and DSA public keys.
+This GRUB module is based on libgcrypt.
+
@node pxe_module
@section pxe
This module provides support for Preboot Execution Environment (PXE) network
@@ -5571,6 +5809,10 @@ This module provides support for the Read-Only Memory File System (ROMFS).
Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more
information.
+@node rsa_sexp_test_module
+@section rsa_sexp_test
+This module provides a test of the libgcrypt RSA functionality in GRUB.
+
@node scsi_module
@section scsi
This module provides support for the Small Computer System Interface (SCSI)
@@ -6414,11 +6656,19 @@ you forget a command, you can run the command @command{help}
@menu
* [:: Check file types and compare values
* acpi:: Load ACPI tables
+* append_add_db_cert:: Add trusted certificate to the db list
+* append_add_db_hash:: Add trusted certificate/binary hash to the db list
+* append_add_dbx_cert:: Add distrusted certificate to the dbx list
+* append_add_dbx_hash:: Add distrusted certificate/binary hash to the dbx list
+* append_list_db:: List all trusted certificates from the db list
+* append_list_dbx:: List all distrusted certificates and binary/certificate hashes from the dbx list
+* append_verify:: Verify appended digital signature using db and dbx lists
* authenticate:: Check whether user is in user list
* background_color:: Set background color for active terminal
* background_image:: Load background image for active terminal
* badram:: Filter out bad regions of RAM
* blocklist:: Print a block list
+* blscfg:: Load Boot Loader Specification menu entries
* boot:: Start up your operating system
* cat:: Show the contents of a file
* clear:: Clear the screen
@@ -6460,7 +6710,10 @@ you forget a command, you can run the command @command{help}
* loopback:: Make a device from a filesystem image
* ls:: List devices or files
* lsfonts:: List loaded fonts
+* lsfreemem:: List free memory blocks
* lsmod:: Show loaded modules
+* lsmem:: List free and allocated memory blocks
+* lsmemregions:: List memory regions
* md5sum:: Compute or check MD5 hash
* module:: Load module for multiboot kernel
* multiboot:: Load multiboot compliant kernel
@@ -6488,12 +6741,14 @@ you forget a command, you can run the command @command{help}
* sleep:: Wait for a specified number of seconds
* smbios:: Retrieve SMBIOS information
* source:: Read a configuration file in same context
+* stress_big_allocs:: Stress test large memory allocations
* test:: Check file types and compare values
* tpm2_key_protector_init:: Initialize the TPM2 key protector
* tpm2_key_protector_clear:: Clear the TPM2 key protector
* tpm2_dump_pcr:: Dump TPM2 PCRs
* true:: Do nothing, successfully
* trust:: Add public key to list of trusted keys
+* uki:: Load Unified Kernel Image menu entries
* unset:: Unset an environment variable
@comment * vbeinfo:: List available video modes
* verify_detached:: Verify detached digital signature
@@ -6535,6 +6790,140 @@ Note: The command is not allowed when lockdown is enforced (@pxref{Lockdown}).
unsigned code.
@end deffn
+@node append_add_db_cert
+@subsection append_add_db_cert
+
+@deffn Command append_add_db_cert
+Read an X.509 certificate from the file @var{X509_certificate}
+and add it to GRUB's internal db list of trusted certificates.
+These certificates are used to validate appended signatures when the
+environment variable @code{check_appended_signatures} (@pxref{check_appended_signatures})
+is set to @code{yes} or the @command{append_verify} (@pxref{append_verify})
+command is executed from the GRUB console.
+
+@xref{Using appended signatures} for more information.
+@end deffn
+
+@node append_add_db_hash
+@subsection append_add_db_hash
+
+@deffn Command append_add_db_hash
+Read a binary hash from the file @var{hash_file}
+and add it to GRUB's internal db list of trusted binary hashes. These
+hashes are used to validate the Linux kernel/GRUB module binary hashes when the
+environment variable @code{check_appended_signatures}
+(@pxref{check_appended_signatures}) is set to @code{yes} or the
+@command{append_verify} (@pxref{append_verify}) command is executed
+from the GRUB console.
+
+Here is an example for how to generate a SHA-256 hash for a file. The hash
+will be in binary format:
+
+@example
+
+# The vmlinux (kernel image) file is your binary file, and
+# it should be unsigned.
+#
+# Generate the binary_hash.bin file from the vmlinux file
+# using OpenSSL command
+
+openssl dgst -binary -sha256 -out binary_hash.bin vmlinux
+
+@end example
+
+@xref{Using appended signatures} for more information.
+@end deffn
+
+@node append_add_dbx_cert
+@subsection append_add_dbx_cert
+
+@deffn Command append_add_dbx_cert
+Read an X.509 certificate from the file @var{X509_certificate}
+and add it to GRUB's internal dbx list of distrusted certificates.
+These certificates are used to ensure that the distrusted certificates
+are rejected during appended signatures validation when the environment
+variable @code{check_appended_signatures} is set to @code{yes}
+(@pxref{check_appended_signatures}) or the @command{append_verify}
+(@pxref{append_verify}) command is executed from the GRUB console.
+Also, these certificates are used to prevent distrusted certificates from
+being added to the db list later on.
+
+@xref{Using appended signatures} for more information.
+@end deffn
+
+@node append_add_dbx_hash
+@subsection append_add_dbx_hash
+
+@deffn Command append_add_dbx_hash [@option{-b}|@option{-c}]
+Read a binary/certificate hash from the file @var{hash_file}
+and add it to GRUB's internal dbx list of distrusted binary/certificate hashes.
+When the environment variable @code{check_appended_signatures} (@pxref{check_appended_signatures})
+is set to @code{yes} or the @command{append_verify} (@pxref{append_verify}) command
+is executed from the GRUB console, then matching distrusted binary hashes or the signature
+validation with distrusted certificates may lead to the rejection of the Linux kernel or GRUB modules.
+Also, these hashes are used to prevent distrusted certificates and binary hashes from being
+added to the db list later on.
+
+The @option{-b} (@option{--binary-hash}) can be used to specify a binary hash file and
+@option{-c} (@option{--cert-hash}) can be used to specify a certificate hash file.
+
+Here is an example for how to generate a SHA-256 hash for a binary and a
+certificate file. The hash will be in binary format:
+
+@example
+
+# The vmlinux (kernel image) file is your binary file, and
+# it should be unsigned. The kernel.der is your certificate file.
+#
+# Generate the cert_hash.bin file from the kernel.der file
+
+openssl dgst -binary -sha256 -out cert_hash.bin kernel.der
+
+# Generate the binary_hash.bin file from the vmlinux file
+
+openssl dgst -binary -sha256 -out binary_hash.bin vmlinux
+
+@end example
+
+@xref{Using appended signatures} for more information.
+@end deffn
+
+@node append_list_db
+@subsection append_list_db
+
+@deffn Command append_list_db
+List all X.509 certificates and binary hashes trusted by GRUB for validating
+appended signatures. The output is a numbered list of certificates and binary hashes,
+showing the certificate's version, serial number, issuer, subject,
+public key algorithm, RSA public key size, and certificate fingerprint.
+
+@xref{Using appended signatures} for more information.
+@end deffn
+
+@node append_list_dbx
+@subsection append_list_dbx
+
+@deffn Command append_list_dbx
+List all the distrusted X.509 certificates and binary/certificate hashes.
+The output is a numbered list of certificates and binary/certificate hashes,
+showing the certificate's version, serial number, issuer, subject,
+public key algorithm, RSA public key size, and certificate fingerprint.
+
+@xref{Using appended signatures} for more information.
+@end deffn
+
+@node append_verify
+@subsection append_verify
+
+@deffn Command append_verify
+Verifies an appended signature on @var{signed_file} against the trusted X.509 certificates
+and hashes known to GRUB (@pxref{append_list_db},@pxref{append_list_dbx}, @pxref{append_add_db_cert},
+@pxref{append_add_db_hash}, @pxref{append_add_dbx_hash} and @pxref{append_add_dbx_cert}).
+Exit code @code{$?} is set to 0 if the signature validates successfully.
+If validation fails, it is set to a non-zero value.
+
+@xref{Using appended signatures} for more information.
+@end deffn
@node authenticate
@subsection authenticate
@@ -6607,6 +6996,72 @@ Print a block list (@pxref{Block list syntax}) for @var{file}.
@end deffn
+@node blscfg
+@subsection blscfg
+
+@deffn Command blscfg [@option{-p|--path} dir] [@option{-f|--enable-fallback}] [@option{-d|--show-default}] [@option{-n|--show-non-default}] [@option{-e|--entry} file]
+Load Boot Loader Specification (BLS) entries into the GRUB menu. Boot entries
+generated from @command{blscfg} won't interfere with entries from @file{grub.cfg} appearing in
+the GRUB menu. Also, entries generated from @command{blscfg} exists only in memory and
+don't update @file{grub.cfg}.
+
+By default, the BLS entries are stored in the @file{/loader/entries} directory in the
+boot partition. If BLS entries are stored elsewhere, the @option{--path} option can be
+used to check a different directory instead of the default location. If no BLS
+entries are found while using the @option{--path} option, the @option{--enable-fallback} option
+can be used to check for entries in the default location.
+
+The @option{--show-default} option allows the default boot entry to be added to the
+GRUB menu from the BLS entries.
+
+The @option{--show-non-default} option allows non-default boot entries to be added to
+the GRUB menu from the BLS entries.
+
+The @option{--entry} option allows specific boot entries to be added to the GRUB menu
+from the BLS entries.
+
+The @option{--entry}, @option{--show-default}, and @option{--show-non-default} options
+are used to filter which BLS entries are added to the GRUB menu. If none are
+used, all entries in the default location or the location specified by @option{--path}
+will be added to the GRUB menu.
+
+A BLS config file example:
+@example
+# /boot/loader/entries/6a9857a393724b7a981ebb5b8495b9ea-3.8.0-2.fc19.x86_64.conf
+title Fedora 19 (Rawhide)
+sort-key fedora
+machine-id 6a9857a393724b7a981ebb5b8495b9ea
+version 3.8.0-2.fc19.x86_64
+options root=UUID=6d3376e4-fc93-4509-95ec-a21d68011da2 quiet
+architecture x64
+linux /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/linux
+initrd /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/initrd
+@end example
+
+For more information on BLS entry keys as well as other information on BLS,
+see: @uref{https://uapi-group.org/specifications/specs/boot_loader_specification/, The Boot Loader Specification}. For the GRUB, there are a few additional
+BLS entry keys based on the @command{menuentry} command (@pxref{menuentry}).
+
+The @code{grub_class} key may be used any number of times to group menu entries into
+classes. Menu themes may display different classes using different styles.
+
+The @code{grub_users} key grants specific users access to specific menu
+entries. @xref{Security}.
+
+The @code{grub_hotkey} key associates a hotkey with a menu entry.
+@var{key} may be a single letter, or one of the aliases @samp{backspace},
+@samp{tab}, or @samp{delete}.
+
+The @code{grub_args} key can be used for any other argument to be passed as positonal
+parameters when the list of commands generated from the BLS config file are
+executed.
+
+Variable expansion using the @samp{$} character (@xref{Shell-like scripting}) may be
+used with BLS config files for the GRUB but might not be compatible with other
+bootloaders.
+@end deffn
+
+
@node boot
@subsection boot
@@ -6756,7 +7211,7 @@ The option @option{--quiet} can be given to suppress the output.
@node cryptomount
@subsection cryptomount
-@deffn Command cryptomount [ [@option{-p} password] | [@option{-k} keyfile [@option{-O} keyoffset] [@option{-S} keysize] ] | [@option{-P} protector] ] [@option{-H} file] device|@option{-u} uuid|@option{-a}|@option{-b}
+@deffn Command cryptomount [ [@option{-p} password] | [@option{-k} keyfile [@option{-O} keyoffset] [@option{-S} keysize] ] | [@option{-P} protector] | [@option{-A}] ] [@option{-H} file] device|@option{-u} uuid|@option{-a}|@option{-b}
Setup access to encrypted device. A passphrase will be requested interactively,
if neither the @option{-p} nor @option{-k} options are given. The option
@option{-p} can be used to supply a passphrase (useful for scripts).
@@ -6764,7 +7219,8 @@ Alternatively the @option{-k} option can be used to supply a keyfile with
options @option{-O} and @option{-S} optionally supplying the offset and size,
respectively, of the key data in the given key file. Besides the keyfile,
the key can be stored in a key protector, and option @option{-P} configures
-specific key protector, e.g. tpm2, to retrieve the key from.
+specific key protector, e.g. tpm2, to retrieve the key from. The option @option{-A}
+enables hardware acceleration in libgcrypt to speed up decryption.
The @option{-H} options can be used to supply cryptomount backends with an
alternative header file (aka detached header). Not all backends have headers
nor support alternative header files (currently only LUKS1 and LUKS2 support them).
@@ -6854,7 +7310,7 @@ These keys are used to validate signatures when environment variable
@code{check_signatures} is set to @code{enforce}
(@pxref{check_signatures}), and by some invocations of
@command{verify_detached} (@pxref{verify_detached}). @xref{Using
-digital signatures}, for more information.
+GPG-style digital signatures}, for more information.
@end deffn
@node drivemap
@@ -7270,7 +7726,7 @@ The output is in GPG's v4 key fingerprint format (i.e., the output of
@code{gpg --fingerprint}). The least significant four bytes (last
eight hexadecimal digits) can be used as an argument to
@command{distrust} (@pxref{distrust}).
-@xref{Using digital signatures}, for more information about uses for
+@xref{Using GPG-style digital signatures}, for more information about uses for
these keys.
@end deffn
@@ -7305,8 +7761,15 @@ When used with care, @option{--skip-sig} and the whitelist enable an
administrator to configure a system to boot only signed
configurations, but to allow the user to select from among multiple
configurations, and to enable ``one-shot'' boot attempts and
-``savedefault'' behavior. @xref{Using digital signatures}, for more
+``savedefault'' behavior. @xref{Using GPG-style digital signatures}, for more
information.
+
+If the environment variable @code{check_appended_signatures} value is set to
+@code{yes} and GRUB is in lockeddown mode, the user is not allowed to set
+@code{check_appended_signatures} to @code{no} and @code{appendedsig_key_mgmt}
+to @code{static} or @code{dynamic} either directly using @command{load_env}
+command or via environment block file. @xref{Using appended signatures}, for
+more information.
@end deffn
@@ -7364,6 +7827,14 @@ List loaded fonts.
@end deffn
+@node lsfreemem
+@subsection lsfreemem
+
+@deffn Command lsfreemem
+List free memory blocks.
+@end deffn
+
+
@node lsmod
@subsection lsmod
@@ -7371,6 +7842,24 @@ List loaded fonts.
Show list of loaded modules.
@end deffn
+
+@node lsmem
+@subsection lsmem
+
+@deffn Command lsmem
+List free and allocated memory blocks.
+@end deffn
+
+
+@node lsmemregions
+@subsection lsmemregions
+
+@deffn Command lsmemregions
+Prints memory region general information including size, number of
+blocks, and total free / total allocated memory per region.
+@end deffn
+
+
@node md5sum
@subsection md5sum
@@ -7677,7 +8166,7 @@ read. It is possible to modify a digitally signed environment block
file from within GRUB using this command, such that its signature will
no longer be valid on subsequent boots. Care should be taken in such
advanced configurations to avoid rendering the system
-unbootable. @xref{Using digital signatures}, for more information.
+unbootable. @xref{Using GPG-style digital signatures}, for more information.
@end deffn
@@ -8012,6 +8501,14 @@ will not be shown immediately.
@end deffn
+@node stress_big_allocs
+@subsection stress_big_allocs
+
+@deffn Command stress_big_allocs
+Stress test large memory allocations.
+@end deffn
+
+
@node test
@subsection test
@@ -8084,7 +8581,7 @@ either @var{expression1} or @var{expression2} is true
@node tpm2_key_protector_init
@subsection tpm2_key_protector_init
-@deffn Command tpm2_key_protector_init [@option{--mode} | @option{-m} mode] | [@option{--pcrs} | @option{-p} pcrlist] | [@option{--bank} | @option{-b} pcrbank] | [ [@option{--tpm2key} | @option{-T} tpm2key_file] | [@option{--keyfile} | @option{-k} keyfile] ] | [@option{--srk} | @option{-s} handle] | [@option{--asymmetric} | @option{-a} srk_type] | [@option{--nvindex} | @option{-n} nv_index]
+@deffn Command tpm2_key_protector_init [@option{--mode} | @option{-m} mode] | [@option{--pcrs} | @option{-p} pcrlist] | [@option{--bank} | @option{-b} pcrbank] | [@option{--cap-pcrs} | @option{-c} pcrlist] | [ [@option{--tpm2key} | @option{-T} tpm2key_file] | [@option{--keyfile} | @option{-k} keyfile] ] | [@option{--srk} | @option{-s} handle] | [@option{--asymmetric} | @option{-a} srk_type] | [@option{--nvindex} | @option{-n} nv_index]
Initialize the TPM2 key protector to unseal the key for the @command{cryptomount}
(@pxref{cryptomount}) command. There are two supported modes,
SRK(@kbd{srk}) and NV index(@kbd{nv}), to be specified by the option
@@ -8099,6 +8596,24 @@ bank that the key is sealed with. The PCR list is a comma-separated list, e.g.,
bank is chosen by selecting a hash algorithm. The current supported PCR banks
are SHA1, SHA256, SHA384, and SHA512, and the default is SHA256.
+The @option{-c} option is introduced to enable the "capping" of a specified list of
+PCRs. This feature addresses scenarios where a user wants to ensure a sealed key
+cannot be unsealed again after its initial use. When the @option{-c} option is
+employed, and the key is successfully unsealed, the TPM2 key protector automatically
+extends the selected PCRs with an EV_SEPARATOR event. This action cryptographically
+alters the PCR values, thereby preventing the associated key from being unsealed in
+any subsequent attempts until those specific PCRs are reset to their original state,
+which typically occurs during a system reboot. In general, it is sufficient to
+extend one associated PCR to cap the key.
+
+It's noteworthy that a key sealed against PCR 8 naturally incorporates a "capping"
+behavior, even without explicitly using a @option{-c} option. This is because GRUB
+measures all commands into PCR 8, including those from configuration files. As a
+result, the value of PCR 8 changes with virtually every command execution during
+the boot process. Consequently, a key sealed against PCR 8 can only be unsealed
+once in a given boot session, as any subsequent GRUB command will alter PCR 8,
+invalidating the unsealing policy and effectively "capping" the key.
+
Some options are only available for the specific mode. The SRK-specific
options are @option{-T}, @option{-k}, @option{-a}, and @option{-s}. On the
other hand, the NV index-specific option is @option{-n}.
@@ -8167,11 +8682,43 @@ signatures when environment variable @code{check_signatures} is set to
must itself be properly signed. The @option{--skip-sig} option can be
used to disable signature-checking when reading @var{pubkey_file}
itself. It is expected that @option{--skip-sig} is useful for testing
-and manual booting. @xref{Using digital signatures}, for more
+and manual booting. @xref{Using GPG-style digital signatures}, for more
information.
@end deffn
+@node uki
+@subsection uki
+
+@deffn Command uki [@option{-p|--path} dir] [@option{-f|--enable-fallback}] [@option{-d|--show-default}] [@option{-n|--show-non-default}] [@option{-e|--entry} file]
+Load Unified Kernel Image (UKI) files into the GRUB menu. Boot entries
+generated from @command{uki} won't interfere with entries from @file{grub.cfg} appearing in the
+GRUB menu. Also, entries generated from @command{uki} exists only in memory and don't
+update @file{grub.cfg}.
+
+By default, the UKI files are stored in the @file{/EFI/Linux} directory in the EFI
+system partition. If UKI files are stored elsewhere, the @option{--path} option can be
+used to check a different directory instead of the default location. If no UKI
+files are found while using the @option{--path} option, the @option{--enable-fallback} option can
+be used to check for files in the default location.
+
+The @option{--show-default} option allows the default boot entry to be added to the
+GRUB menu from the UKI files.
+
+The @option{--show-non-default} option allows non-default boot entries to be added to
+the GRUB menu from the UKI files.
+
+The @option{--entry} option allows specific boot entries to be added to the GRUB menu
+from the UKI files.
+
+The @option{--entry}, @option{--show-default}, and @option{--show-non-default} options
+are used to filter which UKI files are added to the GRUB menu. If none are
+used, all files in the default location or the location specified by @option{--path}
+will be added to the GRUB menu.
+
+For more information on UKI, see: @uref{https://uapi-group.org/specifications/specs/unified_kernel_image/, The Unified Kernel Image Specification}
+@end deffn
+
@node unset
@subsection unset
@@ -8208,7 +8755,7 @@ tried.
Exit code @code{$?} is set to 0 if the signature validates
successfully. If validation fails, it is set to a non-zero value.
-@xref{Using digital signatures}, for more information.
+@xref{Using GPG-style digital signatures}, for more information.
@end deffn
@node videoinfo
@@ -8668,13 +9215,17 @@ environment variables and commands are listed in the same order.
@chapter Security
@menu
-* Authentication and authorisation:: Users and access control
-* Using digital signatures:: Booting digitally signed code
-* UEFI secure boot and shim:: Booting digitally signed PE files
-* Secure Boot Advanced Targeting:: Embedded information for generation number based revocation
-* Measured Boot:: Measuring boot components
-* Lockdown:: Lockdown when booting on a secure setup
-* TPM2 key protector:: Managing disk key with TPM2 key protector
+* Authentication and authorisation:: Users and access control
+* Using GPG-style digital signatures:: Booting digitally signed code
+* Using appended signatures:: An alternative approach to booting digitally signed code
+* UEFI secure boot and shim:: Booting digitally signed PE files
+* Secure Boot Advanced Targeting:: Embedded information for generation number based revocation
+* Measured Boot:: Measuring boot components
+* Lockdown:: Lockdown when booting on a secure setup
+* TPM2 key protector:: Managing disk key with TPM2 key protector
+* Signing certificate and hash files:: Certificate and hash file signing
+* Signing GRUB itself:: Ensuring the integrity of the GRUB core image
+* Hardening:: Configuration and customization to maximize security
@end menu
@node Authentication and authorisation
@@ -8750,12 +9301,12 @@ generating configuration files with authentication. You can use
adding @kbd{set superusers=} and @kbd{password} or @kbd{password_pbkdf2}
commands.
-@node Using digital signatures
-@section Using digital signatures in GRUB
+@node Using GPG-style digital signatures
+@section Using GPG-style digital signatures in GRUB
GRUB's @file{core.img} can optionally provide enforcement that all files
subsequently read from disk are covered by a valid digital signature.
-This document does @strong{not} cover how to ensure that your
+This section does @strong{not} cover how to ensure that your
platform's firmware (e.g., Coreboot) validates @file{core.img}.
If environment variable @code{check_signatures}
@@ -8834,6 +9385,127 @@ or BIOS) configuration to cause the machine to boot from a different
(attacker-controlled) device. GRUB is at best only one link in a
secure boot chain.
+@node Using appended signatures
+@section Using appended signatures in GRUB
+
+GRUB supports verifying Linux-style 'appended signatures' for Linux on Power LPAR
+secure boot. Appended signatures are PKCS#7 messages containing a signature over the
+contents of a file, plus some metadata, appended to the end of a file. A file
+with an appended signature ends with the magic string:
+
+@example
+~Module signature appended~\n
+@end example
+
+where @code{\n} represents the line feed character, @code{0x0a}.
+
+Linux on Power LPAR secure boot is controlled by @strong{'ibm,secure-boot'}
+device tree property and if this property is set to @code{2} (@samp{enforce}),
+GRUB enters lockdown mode. There are three secure boot modes. They are
+
+@itemize
+@item @samp{0 - disabled}: Secure boot is disabled. This is the default.
+@item @samp{1 - audit}: Enforce signature verification by setting
+ @code{check_appended_signatures} (@pxref{check_appended_signatures}) to
+ @code{yes} and do not enter lockdown mode. Signature verification
+ is performed and if signature verification fails, display the errors and
+ allow the boot to continue.
+@item @samp{2 - enforce}: Enter lockdown mode and enforce signature verification by setting
+ @code{check_appended_signatures} (@pxref{check_appended_signatures}) to @code{yes}.
+@end itemize
+
+Note that Linux on Power LPAR only supports @samp{0 - disabled} and @samp{2 - enforce},
+and @samp{1 - audit} is considered as secure boot being disabled.
+
+Enforcement of signature verification is controlled by the environment variable
+@code{check_appended_signatures} (@pxref{check_appended_signatures}).
+
+@itemize
+@item @samp{no}: No verification is performed. This is the default.
+@item @samp{yes}: Signature verification is performed and if signature verification fails,
+ display the errors and stop the boot. Signature verification cannot be disabled by setting
+ the @code{check_appended_signatures} variable back to @samp{no}.
+@end itemize
+
+To enable appended signature verification, load the appendedsig module and an
+X.509 certificate for verification. It is recommended to build the appendedsig module
+into the core GRUB image.
+
+Key management is controlled by the environment variable @code{appendedsig_key_mgmt}
+(@pxref{appendedsig_key_mgmt}).
+
+@itemize
+@item @samp{static}: Enforce static key management signature verification. This is the default.
+ When GRUB is in lockdown mode, then the user cannot change the value of the
+ @code{appendedsig_key_mgmt}.
+@item @samp{dynamic}: Enforce dynamic key management signature verification. When GRUB is in
+ lockdown mode, then the user cannot change the value of the @code{appendedsig_key_mgmt}.
+@end itemize
+
+In static key management mode, certificates will be built into the core image using
+the @code{--x509} parameter to @command{grub-mkimage}. The list of trusted certificates
+available at boot time can be shown using @command{append_list_db} (@pxref{append_list_db}).
+Distrusted certificates can be explicitly removed from the db using @command{append_add_dbx_cert}
+(@pxref{append_add_dbx_cert}). Also, trusted certificates can be explicitly added to the db using
+@command{append_add_db_cert} (@pxref{append_add_db_cert}).
+
+In dynamic key management mode, db and dbx are read from the Platform KeyStore (PKS). If
+db does not exist in PKS, static keys (built-in keys) are used as the default keys.
+The list of trusted certificates and binary hashes available at boot time can be shown using
+@command{append_list_db} (@pxref{append_list_db}) and the list of distrusted certificates and
+binary/certificate hashes available at boot time can be shown using @command{append_list_dbx}
+(@pxref{append_list_dbx}). The trusted certificates and binary hashes can be explicitly added
+to the db using @command{append_add_db_cert} (@pxref{append_add_db_cert}) and
+@command{append_add_db_hash} (@pxref{append_add_db_hash}). Distrusted certificates can be explicitly
+added to the dbx using @command{append_add_dbx_cert} (@pxref{append_add_dbx_cert}) and distrusted
+certificate/binary hashes can be explicitly added to the dbx using @command{append_add_dbx_hash}
+(@pxref{append_add_dbx_hash}).
+
+A file can be explicitly verified using @command{append_verify} (@pxref{append_verify}).
+
+Note that when the environment variable @code{check_appended_signatures} is set to @code{yes},
+the @command{append_add_db_cert} and @command{append_add_dbx_cert} commands only accept
+the file @samp{@var{X509_certificate}} that is signed with an appended signature
+(@pxref{Signing certificate and hash files}), and the @command{append_add_db_hash} and
+@command{append_add_dbx_hash} commands only accept the file @samp{@var{hash_file}} that is
+signed with an appended signature (@pxref{Signing certificate and hash files}).
+The signature is verified by the appendedsig module.
+When the environment variable @code{check_appended_signatures} is set to @code{no},
+these commands accept files without an appended signature.
+
+Also, note that @samp{@var{X509_certificate}} should be in DER-format and @samp{@var{hash_file}}
+should be in binary format. Only SHA-256, SHA-384, or SHA-512 hashes of binary/certificate are allowed.
+Certificates/hashes of certificates/binaries added through @command{append_add_db_cert},
+@command{append_add_dbx_cert}, @command{append_add_db_hash}, and @command{append_add_dbx_hash}
+will not be persisted across boots.
+
+Only signatures created using SHA-256 or SHA-512 hash algorithm along with RSA keys of size 2048,
+3072, or 4096 bits are supported.
+
+A file can be signed with the @command{sign-file} utility supplied with the
+Linux kernel source. For example, if you have @code{signing.key} as the private
+key and @code{certificate.der} as the X.509 certificate containing the public key:
+
+@example
+sign-file SHA256 signing.key certificate.der vmlinux vmlinux.signed
+@end example
+
+Once signature verification is turned on, the following file types must carry
+appended signatures:
+
+@enumerate
+@item Linux kernels
+@item GRUB modules, except those built in to the core image
+@item Any new certificate or binary hash files to be trusted
+@item Any new certificate/binary hash files to be distrusted
+@end enumerate
+
+When GRUB is in lockdown mode (when secure boot mode is set to @code{enforce}),
+signature verification cannot be @strong{disabled} by setting the
+@code{check_appended_signatures} (@pxref{check_appended_signatures}) variable
+to @code{no} or using the @command{load_env} (@pxref{load_env}) command from
+the GRUB console.
+
@node UEFI secure boot and shim
@section UEFI secure boot and shim support
@@ -8912,7 +9584,7 @@ platforms.
@section Lockdown when booting on a secure setup
The GRUB can be locked down when booted on a secure boot environment, for example
-if the UEFI secure boot is enabled. On a locked down configuration, the GRUB will
+if UEFI or Power secure boot is enabled. On a locked down configuration, the GRUB will
be restricted and some operations/commands cannot be executed. This also includes
limiting which filesystems are supported to those thought to be more robust and
widely used within GRUB.
@@ -9363,6 +10035,189 @@ which increases the risk of password leakage during the process. Moreover, the
superuser list must be well maintained, and the password used cannot be
synchronized with LUKS key rotation.
+@node Signing certificate and hash files
+@section Signing certificate and hash files
+X.509 certificate (public key) files and hash files (binary/certificate hash files)
+can be signed with a Linux kernel module-style appended signature.
+
+The signer.key is a private key used for signing and signer.der is the corresponding
+public key (certificate) used for appended signature verification. Note that the
+signer.der (certificate) should exist in the db (@pxref{Using appended signatures}).
+
+@itemize
+@item Signing the X.509 certificate file using @file{sign-file}.
+The kernel.der is an X.509 certificate file.
+@example
+
+sign-file SHA256 signer.key signer.der kernel.der \
+ kernel.der.signed
+
+@end example
+@item Signing the hash file using @file{sign-file}.
+The binary_hash.bin is a binary hash file.
+@example
+
+sign-file SHA256 signer.key signer.der binary_hash.bin \
+ binary_hash.signed
+
+@end example
+@end itemize
+
+@node Signing GRUB itself
+@section Signing GRUB itself
+To ensure a complete secure-boot chain, there must be a way for the code that
+loads GRUB to verify the integrity of the core image.
+This is ultimately platform-specific and individual platforms can define their
+own mechanisms. However, there are general-purpose mechanisms that can be used
+with GRUB.
+
+@subsection Signing GRUB for UEFI secure boot
+On UEFI platforms, @file{core.img} is a PE binary. Therefore, it can be signed
+with a tool such as @command{pesign} or @command{sbsign}. Refer to the
+suggestions in @pxref{UEFI secure boot and shim} to ensure that the final
+image works under UEFI secure boot and can maintain the secure-boot chain. It
+will also be necessary to enroll the public key used into a relevant firmware
+key database.
+
+@subsection Signing GRUB with an appended signature
+The @file{core.elf} itself can be signed with a Linux kernel module-style
+appended signature (@pxref{Using appended signatures}).
+To support IEEE1275 platforms where the boot image is often loaded directly
+from a disk partition rather than from a file system, the @file{core.elf}
+can specify the size and location of the appended signature with an ELF
+Note added by @command{grub-install} or @command{grub-mkimage}.
+An image can be signed this way using the @command{sign-file} command from
+the Linux kernel:
+
+@itemize
+@item Signing a GRUB image using a single signer key. The grub.key is your
+private key used for GRUB signing, grub.der is a corresponding public key
+(certificate) used for GRUB signature verification, and the kernel.der is
+your public key (certificate) used for kernel signature verification.
+@example
+@group
+# Determine the size of the appended signature. It depends on the
+# signing key and the hash algorithm.
+#
+# Signing /dev/null with an appended signature.
+
+sign-file SHA256 grub.key grub.der /dev/null ./empty.sig
+
+# Build a GRUB image for the signature.
+
+grub-mkimage -O powerpc-ieee1275 -o core.elf.unsigned -x kernel.der \
+ -p /grub --appended-signature-size $(stat -c '%s' ./empty.sig) \
+ --modules="appendedsig ..." ...
+
+# Remove the signature file.
+
+rm ./empty.sig
+
+# Signing a GRUB image with an appended signature.
+
+sign-file SHA256 grub.key grub.der core.elf.unsigned core.elf.signed
+
+@end group
+@end example
+@item Signing a GRUB image using more than one signer key. The grub1.key and
+grub2.key are private keys used for GRUB signing, grub1.der and grub2.der
+are corresponding public keys (certificates) used for GRUB signature verification.
+The kernel1.der and kernel2.der are your public keys (certificates) used for
+kernel signature verification.
+@example
+@group
+# Generate a signature by signing /dev/null.
+
+openssl cms -sign -binary -nocerts -in /dev/null -signer \
+ grub1.der -inkey grub1.key -signer grub2.der -inkey grub2.key \
+ -out ./empty.p7s -outform DER -noattr -md sha256
+
+# To be able to determine the size of an appended signature, sign an
+# empty file (/dev/null) to which a signature will be appended to.
+
+sign-file -s ./empty.p7s sha256 /dev/null /dev/null ./empty.sig
+
+# Build a GRUB image for the signature.
+
+grub-mkimage -O powerpc-ieee1275 -o core.elf.unsigned -x kernel1.der \
+ kernel2.der -p /grub --appended-signature-size $(stat -c '%s' ./empty.sig) \
+ --modules="appendedsig ..." ...
+
+# Remove the signature files.
+
+rm ./empty.sig ./empty.p7s
+
+# Generate a raw signature for GRUB image signing using OpenSSL.
+
+openssl cms -sign -binary -nocerts -in core.elf.unsigned -signer \
+ grub1.der -inkey grub1.key -signer grub2.der -inkey grub2.key \
+ -out core.p7s -outform DER -noattr -md sha256
+
+# Sign a GRUB image to get an image file with an appended signature.
+
+sign-file -s core.p7s sha256 /dev/null core.elf.unsigned core.elf.signed
+
+@end group
+@end example
+@item Don't forget to install the signed image as required
+(e.g. on powerpc-ieee1275, to the PReP partition).
+@example
+@group
+# Install signed GRUB image to the PReP partition on powerpc-ieee1275
+
+dd if=core.elf.signed of=/dev/sda1
+
+@end group
+@end example
+@end itemize
+
+As with UEFI secure boot, it is necessary to build-in the required modules,
+or sign them if they are not part of the GRUB image.
+
+@node Hardening
+@section Hardening
+
+Security hardening involves additional / optional configuration and
+customization steps to GRUB to maximize security. The extent to which
+hardening can be accomplished depends on the threats attempting to be
+mitigated for a given system / device, the device architecture, and number
+of GRUB features required. The following is a listing of hardening steps which
+may be considered:
+
+@itemize
+@item (EFI Only) Enable secure boot to enable lockdown mode. This will limit
+the attack surface of GRUB by limiting the commands and file systems
+supported. (@pxref{Lockdown})
+@item (EFI Only) No-Execute capability of memory segments will be configured
+by GRUB as indicated by the UEFI. This makes some classes of vulnerabilities
+more difficult to exploit by providing support for marking memory as either
+writable or executable.
+@item (EFI Only) While building GRUB, the stack protector feature may be
+enabled during the configuration step. This feature can make certain
+vulnerabilities caused by stack buffer overflows more difficult to exploit.
+This can be enabled by including the "--enable-stack-protector" flag to the
+configure script:
+@example
+# @kbd{./configure --enable-stack-protector}
+@end example
+Please reference the file @file{INSTALL} for detailed instructions on how to
+build GRUB.
+@item Minimize the installed modules included with the GRUB installation.
+For instance, if a specific file system is used for a given system, modules
+for other file systems may be excluded. @pxref{Modules} for a list of
+modules.
+@item Minimize boot sources. In the GRUB configuration, reduce the possible
+boot sources to the minimum needed for system operation. For instance, if
+booting only from an internal drive, remove support for network booting
+and booting from removable media.
+@item Disable network support in GRUB if not required. Ensure network
+interfaces are not configured in the GRUB configuration and consider
+setting environment variable @samp{feature_net_search_cfg} to @samp{n} in an
+embedded GRUB config file in order to disable attempting to use the
+network for obtaining a GRUB config file.
+@end itemize
+
+
@node Platform limitations
@chapter Platform limitations
@@ -9519,10 +10374,10 @@ to install to is specified, UUID is used instead as well.
@end multitable
@node Platform-specific operations
-@chapter Outline
+@chapter Platform-specific operations
-Some platforms have features which allows to implement
-some commands useless or not implementable on others.
+Some platforms have features which allow implementation of
+certain commands that cannot be implemented on others.
Quick summary:
@@ -10456,16 +11311,16 @@ Print each line of input after reading it.
@strong{Caution:} GRUB requires binutils-2.9.1.0.23 or later because the
GNU assembler has been changed so that it can produce real 16bits
machine code between 2.9.1 and 2.9.1.0.x. See
-@uref{http://sources.redhat.com/binutils/}, to obtain information on
+@uref{https://www.gnu.org/software/binutils/}, to obtain information on
how to get the latest version.
@end quotation
GRUB is available from the GNU alpha archive site
-@uref{ftp://ftp.gnu.org/gnu/grub} or any of its mirrors. The file
+@uref{https://ftp.gnu.org/gnu/grub/} or any of its mirrors. The file
will be named grub-version.tar.gz. The current version is
@value{VERSION}, so the file you should grab is:
-@uref{ftp://ftp.gnu.org/gnu/grub/grub-@value{VERSION}.tar.gz}
+@uref{https://ftp.gnu.org/gnu/grub/grub-@value{VERSION}.tar.gz}
To unbundle GRUB use the instruction:
@@ -10487,7 +11342,7 @@ just do:
@end example
Also, the latest version is available using Git. See
-@uref{http://www.gnu.org/software/grub/grub-download.html} for more
+@uref{https://www.gnu.org/software/grub/grub-download.html} for more
information.
@node Reporting bugs
@@ -10499,7 +11354,7 @@ list below before you submit bugs:
@enumerate
@item
Before getting unsettled, read this manual through and through. Also,
-see the @uref{http://www.gnu.org/software/grub/grub-faq.html, GNU GRUB FAQ}.
+see the @uref{https://www.gnu.org/software/grub/grub-faq.html, GNU GRUB FAQ}.
@item
Always mention the information on your GRUB. The version number and the
@@ -10549,7 +11404,7 @@ important.
@end enumerate
If you follow the guideline above, submit a report to the
-@uref{http://savannah.gnu.org/bugs/?group=grub, Bug Tracking System}.
+@uref{https://savannah.gnu.org/bugs/?group=grub, Bug Tracking System}.
Alternatively, you can submit a report via electronic mail to
@email{bug-grub@@gnu.org}, but we strongly recommend that you use the
Bug Tracking System, because e-mail can be passed over easily.
@@ -10561,10 +11416,10 @@ Once we get your report, we will try to fix the bugs.
@appendix Where GRUB will go
GRUB 2 is now quite stable and used in many production systems. We are
-currently working towards a 2.0 release.
+currently working on the 2.x series.
If you are interested in the development of GRUB 2, take a look at
-@uref{http://www.gnu.org/software/grub/grub.html, the homepage}.
+@uref{https://www.gnu.org/software/grub/grub.html, the homepage}.
diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
index e50db8106..8577462d5 100644
--- a/grub-core/Makefile.am
+++ b/grub-core/Makefile.am
@@ -246,6 +246,8 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/alloc.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h
+KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/powerpc/ieee1275/ieee1275.h
+KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/powerpc/ieee1275/platform_keystore.h
endif
if COND_sparc64_ieee1275
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index b3f71196a..0cf155128 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -248,6 +248,7 @@ kernel = {
xen = term/xen/console.c;
xen = disk/xen/xendisk.c;
xen = commands/boot.c;
+ xen = kern/xen/cmdline.c;
i386_xen_pvh = commands/boot.c;
i386_xen_pvh = disk/xen/xendisk.c;
@@ -255,6 +256,7 @@ kernel = {
i386_xen_pvh = kern/i386/xen/tsc.c;
i386_xen_pvh = kern/i386/xen/pvh.c;
i386_xen_pvh = kern/xen/init.c;
+ i386_xen_pvh = kern/xen/cmdline.c;
i386_xen_pvh = term/xen/console.c;
ia64_efi = kern/ia64/efi/startup.S;
@@ -331,6 +333,9 @@ kernel = {
powerpc_ieee1275 = kern/powerpc/cache.S;
powerpc_ieee1275 = kern/powerpc/dl.c;
powerpc_ieee1275 = kern/powerpc/compiler-rt.S;
+ powerpc_ieee1275 = kern/lockdown.c;
+ powerpc_ieee1275 = kern/powerpc/ieee1275/ieee1275.c;
+ powerpc_ieee1275 = kern/powerpc/ieee1275/platform_keystore.c;
sparc64_ieee1275 = kern/sparc64/cache.S;
sparc64_ieee1275 = kern/sparc64/dl.c;
@@ -367,6 +372,9 @@ kernel = {
emu = kern/emu/cache_s.S;
emu = kern/emu/hostdisk.c;
emu = osdep/unix/hostdisk.c;
+ emu = osdep/relpath.c;
+ emu = osdep/getroot.c;
+ emu = osdep/unix/getroot.c;
emu = osdep/exec.c;
extra_dist = osdep/unix/exec.c;
emu = osdep/devmapper/hostdisk.c;
@@ -845,6 +853,18 @@ module = {
common = commands/blocklist.c;
};
+module = {
+ name = blsuki;
+ common = commands/blsuki.c;
+ common = lib/gnulib/filevercmp.c;
+ enable = powerpc_ieee1275;
+ enable = efi;
+ enable = i386_pc;
+ enable = emu;
+ cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)';
+ cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB)';
+};
+
module = {
name = boot;
common = commands/boot.c;
@@ -979,6 +999,21 @@ module = {
cppflags = '$(CPPFLAGS_GCRY)';
};
+module = {
+ name = appendedsig;
+ common = commands/appendedsig/appendedsig.c;
+ common = commands/appendedsig/x509.c;
+ common = commands/appendedsig/pkcs7.c;
+ common = commands/appendedsig/asn1util.c;
+ common = commands/appendedsig/gnutls_asn1_tab.c;
+ common = commands/appendedsig/pkix_asn1_tab.c;
+ enable = emu;
+ enable = powerpc_ieee1275;
+ cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls';
+ cppflags = '$(CPPFLAGS_GCRY) -I$(srcdir)/lib/libtasn1-grub';
+ depends = crypto, gcry_rsa, gcry_sha256, gcry_sha512, mpi, asn1;
+};
+
module = {
name = hdparm;
common = commands/hdparm.c;
@@ -1685,6 +1720,8 @@ module = {
module = {
name = crypto;
common = lib/crypto.c;
+ common = lib/hwfeatures-gcry.c;
+ x86_64_efi = lib/x86_64/efi/hwfeatures-gcry.c;
extra_dist = lib/libgcrypt-grub/cipher/crypto.lst;
};
@@ -1694,6 +1731,11 @@ module = {
common = lib/pbkdf2.c;
};
+module = {
+ name = argon2;
+ common = lib/argon2.c;
+};
+
module = {
name = relocator;
common = lib/relocator.c;
@@ -2196,6 +2238,14 @@ module = {
cppflags = '$(CPPFLAGS_GCRY)';
};
+module = {
+ name = appended_signature_test;
+ common = tests/appended_signature_test.c;
+ common = tests/appended_signatures.h;
+ enable = emu;
+ enable = powerpc_ieee1275;
+};
+
module = {
name = signature_test;
common = tests/signature_test.c;
@@ -2217,6 +2267,16 @@ module = {
common = tests/pbkdf2_test.c;
};
+module = {
+ name = argon2_test;
+ common = tests/argon2_test.c;
+};
+
+module = {
+ name = crypto_cipher_mode_test;
+ common = tests/crypto_cipher_mode_test.c;
+};
+
module = {
name = legacy_password_test;
common = tests/legacy_password_test.c;
@@ -2469,6 +2529,13 @@ module = {
cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/minilzo -DMINILZO_HAVE_CONFIG_H';
};
+module = {
+ name = zstdio;
+ common = io/zstdio.c;
+ cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/zstd';
+ cflags='-Wno-unreachable-code';
+};
+
module = {
name = testload;
common = commands/testload.c;
diff --git a/grub-core/boot/decompressor/minilib.c b/grub-core/boot/decompressor/minilib.c
index fc46ee07b..1fe3efaae 100644
--- a/grub-core/boot/decompressor/minilib.c
+++ b/grub-core/boot/decompressor/minilib.c
@@ -50,6 +50,12 @@ grub_memmove (void *dest, const void *src, grub_size_t n)
return dest;
}
+void *
+grub_memcpy (void *dest, const void *src, grub_size_t n)
+{
+ return grub_memmove (dest, src, n);
+}
+
int
grub_memcmp (const void *s1, const void *s2, grub_size_t n)
{
diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c
new file mode 100644
index 000000000..5c53f634c
--- /dev/null
+++ b/grub-core/commands/appendedsig/appendedsig.c
@@ -0,0 +1,1716 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2020, 2021, 2022 Free Software Foundation, Inc.
+ * Copyright (C) 2020, 2021, 2022, 2025 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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "appendedsig.h"
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+/* Public key type. */
+#define PKEY_ID_PKCS7 2
+
+/* Appended signature magic string and size. */
+#define SIG_MAGIC "~Module signature appended~\n"
+#define SIG_MAGIC_SIZE ((sizeof(SIG_MAGIC) - 1))
+
+/* SHA256, SHA384 and SHA512 hash sizes. */
+#define SHA256_HASH_SIZE 32
+#define SHA384_HASH_SIZE 48
+#define SHA512_HASH_SIZE 64
+
+#define OPTION_BINARY_HASH 0
+#define OPTION_CERT_HASH 1
+
+/*
+ * This structure is extracted from scripts/sign-file.c in the linux kernel
+ * source. It was licensed as LGPLv2.1+, which is GPLv3+ compatible.
+ */
+struct module_signature
+{
+ grub_uint8_t algo; /* Public-key crypto algorithm [0]. */
+ grub_uint8_t hash; /* Digest algorithm [0]. */
+ grub_uint8_t id_type; /* Key identifier type [PKEY_ID_PKCS7]. */
+ grub_uint8_t signer_len; /* Length of signer's name [0]. */
+ grub_uint8_t key_id_len; /* Length of key identifier [0]. */
+ grub_uint8_t __pad[3];
+ grub_uint32_t sig_len; /* Length of signature data. */
+} GRUB_PACKED;
+
+#define SIG_METADATA_SIZE (sizeof (struct module_signature))
+#define APPENDED_SIG_SIZE(pkcs7_data_size) \
+ (pkcs7_data_size + SIG_MAGIC_SIZE + SIG_METADATA_SIZE)
+
+/* This represents an entire, parsed, appended signature. */
+struct appended_signature
+{
+ struct module_signature sig_metadata; /* Module signature metadata. */
+ grub_pkcs7_data_t pkcs7; /* Parsed PKCS#7 data. */
+ grub_size_t signature_len; /* Length of PKCS#7 data + metadata + magic. */
+};
+typedef struct appended_signature sb_appendedsig_t;
+
+/* This represents a trusted certificates. */
+struct sb_database
+{
+ grub_x509_cert_t *certs; /* Certificates. */
+ grub_uint32_t cert_entries; /* Number of certificates. */
+ grub_uint8_t **hashes; /* Certificate/binary hashes. */
+ grub_size_t *hash_sizes; /* Sizes of certificate/binary hashes. */
+ grub_uint32_t hash_entries; /* Number of certificate/binary hashes. */
+ bool is_db; /* Flag to indicate the db/dbx list. */
+};
+typedef struct sb_database sb_database_t;
+
+/* The db list is used to validate appended signatures. */
+static sb_database_t db = {.certs = NULL, .cert_entries = 0, .hashes = NULL,
+ .hash_sizes = NULL, .hash_entries = 0, .is_db = true};
+/*
+ * The dbx list is used to ensure that the distrusted certificates or GRUB
+ * modules/kernel binaries are rejected during appended signatures/hashes
+ * validation.
+ */
+static sb_database_t dbx = {.certs = NULL, .cert_entries = 0, .hashes = NULL,
+ .hash_sizes = NULL, .hash_entries = 0, .is_db = false};
+
+/*
+ * Signature verification flag (check_sigs).
+ * check_sigs: false
+ * - No signature verification. This is the default.
+ * check_sigs: true
+ * - Enforce signature verification, and if signature verification fails, post
+ * the errors and stop the boot.
+ */
+static bool check_sigs = false;
+
+/*
+ * append_key_mgmt: Key Management Modes
+ * False: Static key management (use built-in Keys). This is default.
+ * True: Dynamic key management (use Platform KeySotre).
+ */
+static bool append_key_mgmt = false;
+
+/* Platform KeyStore db and dbx. */
+static grub_pks_t *pks_keystore;
+
+/* Appended signature size. */
+static grub_size_t append_sig_len = 0;
+
+static const struct grub_arg_option options[] =
+{
+ {"binary-hash", 'b', 0, N_("hash file of the binary."), 0, ARG_TYPE_PATHNAME},
+ {"cert-hash", 'c', 1, N_("hash file of the certificate."), 0, ARG_TYPE_PATHNAME},
+ {0, 0, 0, 0, 0, 0}
+};
+
+static grub_ssize_t
+pseudo_read (struct grub_file *file, char *buf, grub_size_t len)
+{
+ grub_memcpy (buf, (grub_uint8_t *) file->data + file->offset, len);
+ return len;
+}
+
+/* Filesystem descriptor. */
+static struct grub_fs pseudo_fs = {
+ .name = "pseudo",
+ .fs_read = pseudo_read
+};
+
+/*
+ * We cannot use hexdump() to display hash data because it is typically displayed
+ * in hexadecimal format, along with an ASCII representation of the same data.
+ *
+ * Example: sha256 hash data
+ * 00000000 52 b5 90 49 64 de 22 d7 4e 5f 4f b4 1b 51 9c 34 |R..Id.".N_O..Q.4|
+ * 00000010 b1 96 21 7c 91 78 a5 0d 20 8c e9 5c 22 54 53 f7 |..!|.x.. ..\"TS.|
+ *
+ * An appended signature only required to display the hexadecimal of the hash data
+ * by separating each byte with ":". So, we introduced a new method hexdump_colon
+ * to display it.
+ *
+ * Example: Sha256 hash data
+ * 52:b5:90:49:64:de:22:d7:4e:5f:4f:b4:1b:51:9c:34:
+ * b1:96:21:7c:91:78:a5:0d:20:8c:e9:5c:22:54:53:f7
+ */
+static void
+hexdump_colon (const grub_uint8_t *data, const grub_size_t length)
+{
+ grub_size_t i, count = 0;
+
+ for (i = 0; i < length - 1; i++)
+ {
+ grub_printf ("%02x:", data[i]);
+ count++;
+ if (count == 16)
+ {
+ grub_printf ("\n ");
+ count = 0;
+ }
+ }
+
+ grub_printf ("%02x\n", data[i]);
+}
+
+static void
+print_certificate (const grub_x509_cert_t *cert, const grub_uint32_t cert_num)
+{
+ grub_uint32_t i;
+
+ grub_printf ("\nCertificate: %u\n", cert_num);
+ grub_printf (" Data:\n");
+ grub_printf (" Version: %u (0x%u)\n", cert->version + 1, cert->version);
+ grub_printf (" Serial Number:\n ");
+
+ for (i = 0; i < cert->serial_len - 1; i++)
+ grub_printf ("%02x:", cert->serial[i]);
+
+ grub_printf ("%02x\n", cert->serial[cert->serial_len - 1]);
+ grub_printf (" Issuer: %s\n", cert->issuer);
+ grub_printf (" Subject: %s\n", cert->subject);
+ grub_printf (" Subject Public Key Info:\n");
+ grub_printf (" Public Key Algorithm: rsaEncryption\n");
+ grub_printf (" RSA Public-Key: (%d bit)\n", cert->modulus_size);
+ grub_printf (" Fingerprint: sha256\n ");
+ hexdump_colon (&cert->fingerprint[GRUB_FINGERPRINT_SHA256][0],
+ grub_strlen ((char *) cert->fingerprint[GRUB_FINGERPRINT_SHA256]));
+}
+
+/*
+ * GUID can be used to determine the hashing function and generate the hash using
+ * determined hashing function.
+ */
+static grub_err_t
+get_hash (const grub_packed_guid_t *guid, const grub_uint8_t *data, const grub_size_t data_size,
+ grub_uint8_t *hash, grub_size_t *hash_size)
+{
+ gcry_md_spec_t *hash_func = NULL;
+
+ if (guid == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, "GUID is not available");
+
+ if (grub_memcmp (guid, &GRUB_PKS_CERT_SHA256_GUID, GRUB_PACKED_GUID_SIZE) == 0 ||
+ grub_memcmp (guid, &GRUB_PKS_CERT_X509_SHA256_GUID, GRUB_PACKED_GUID_SIZE) == 0)
+ hash_func = &_gcry_digest_spec_sha256;
+ else if (grub_memcmp (guid, &GRUB_PKS_CERT_SHA384_GUID, GRUB_PACKED_GUID_SIZE) == 0 ||
+ grub_memcmp (guid, &GRUB_PKS_CERT_X509_SHA384_GUID, GRUB_PACKED_GUID_SIZE) == 0)
+ hash_func = &_gcry_digest_spec_sha384;
+ else if (grub_memcmp (guid, &GRUB_PKS_CERT_SHA512_GUID, GRUB_PACKED_GUID_SIZE) == 0 ||
+ grub_memcmp (guid, &GRUB_PKS_CERT_X509_SHA512_GUID, GRUB_PACKED_GUID_SIZE) == 0)
+ hash_func = &_gcry_digest_spec_sha512;
+ else
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, "unsupported GUID hash");
+
+ grub_crypto_hash (hash_func, hash, data, data_size);
+ *hash_size = hash_func->mdlen;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+generate_cert_hash (const grub_size_t cert_hash_size, const grub_uint8_t *data,
+ const grub_size_t data_size, grub_uint8_t *hash, grub_size_t *hash_size)
+{
+ grub_packed_guid_t guid = { 0 };
+
+ /* support SHA256, SHA384 and SHA512 for certificate hash */
+ if (cert_hash_size == SHA256_HASH_SIZE)
+ grub_memcpy (&guid, &GRUB_PKS_CERT_X509_SHA256_GUID, GRUB_PACKED_GUID_SIZE);
+ else if (cert_hash_size == SHA384_HASH_SIZE)
+ grub_memcpy (&guid, &GRUB_PKS_CERT_X509_SHA384_GUID, GRUB_PACKED_GUID_SIZE);
+ else if (cert_hash_size == SHA512_HASH_SIZE)
+ grub_memcpy (&guid, &GRUB_PKS_CERT_X509_SHA512_GUID, GRUB_PACKED_GUID_SIZE);
+ else
+ {
+ grub_dprintf ("appendedsig", "unsupported hash type (%" PRIuGRUB_SIZE ") and "
+ "skipped\n", cert_hash_size);
+ return GRUB_ERR_UNKNOWN_COMMAND;
+ }
+
+ return get_hash (&guid, data, data_size, hash, hash_size);
+}
+
+/* Check the hash presence in the db/dbx list. */
+static bool
+check_hash_presence (grub_uint8_t *const hash, const grub_size_t hash_size,
+ const sb_database_t *sb_database)
+{
+ grub_uint32_t i;
+
+ for (i = 0; i < sb_database->hash_entries; i++)
+ {
+ if (sb_database->hashes[i] == NULL)
+ continue;
+
+ if (hash_size == sb_database->hash_sizes[i] &&
+ grub_memcmp (sb_database->hashes[i], hash, hash_size) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+/* Add the certificate/binary hash into the db/dbx list. */
+static grub_err_t
+add_hash (grub_uint8_t *const data, const grub_size_t data_size, sb_database_t *sb_database)
+{
+ grub_uint8_t **hashes;
+ grub_size_t *hash_sizes;
+
+ if (data == NULL || data_size == 0)
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, "certificate/binary-hash data or size is not available");
+
+ if (sb_database->is_db == true)
+ {
+ if (check_hash_presence (data, data_size, &dbx) == true)
+ {
+ grub_dprintf ("appendedsig",
+ "cannot add a hash (%02x%02x%02x%02x), as it is present in the dbx list\n",
+ data[0], data[1], data[2], data[3]);
+ return GRUB_ERR_ACCESS_DENIED;
+ }
+ }
+
+ if (check_hash_presence (data, data_size, sb_database) == true)
+ {
+ grub_dprintf ("appendedsig",
+ "cannot add a hash (%02x%02x%02x%02x), as it is present in the %s list\n",
+ data[0], data[1], data[2], data[3], ((sb_database->is_db == true) ? "db" : "dbx"));
+ return GRUB_ERR_EXISTS;
+ }
+
+ hashes = grub_realloc (sb_database->hashes, sizeof (grub_uint8_t *) * (sb_database->hash_entries + 1));
+ if (hashes == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
+
+ hash_sizes = grub_realloc (sb_database->hash_sizes, sizeof (grub_size_t) * (sb_database->hash_entries + 1));
+ if (hash_sizes == NULL)
+ {
+ /* Allocated memory will be freed by free_db_list()/free_dbx_list(). */
+ hashes[sb_database->hash_entries] = NULL;
+ sb_database->hashes = hashes;
+ sb_database->hash_entries++;
+
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
+ }
+
+ hashes[sb_database->hash_entries] = grub_malloc (data_size);
+ if (hashes[sb_database->hash_entries] == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
+
+ grub_dprintf ("appendedsig",
+ "added the hash %02x%02x%02x%02x... with size of %" PRIuGRUB_SIZE " to the %s list\n",
+ data[0], data[1], data[2], data[3], data_size,
+ ((sb_database->is_db == true) ? "db" : "dbx"));
+
+ grub_memcpy (hashes[sb_database->hash_entries], data, data_size);
+ hash_sizes[sb_database->hash_entries] = data_size;
+ sb_database->hash_sizes = hash_sizes;
+ sb_database->hashes = hashes;
+ sb_database->hash_entries++;
+
+ return GRUB_ERR_NONE;
+}
+
+static bool
+is_hash (const grub_packed_guid_t *guid)
+{
+ /* GUID type of the binary hash. */
+ if (grub_memcmp (guid, &GRUB_PKS_CERT_SHA256_GUID, GRUB_PACKED_GUID_SIZE) == 0 ||
+ grub_memcmp (guid, &GRUB_PKS_CERT_SHA384_GUID, GRUB_PACKED_GUID_SIZE) == 0 ||
+ grub_memcmp (guid, &GRUB_PKS_CERT_SHA512_GUID, GRUB_PACKED_GUID_SIZE) == 0)
+ return true;
+
+ /* GUID type of the certificate hash. */
+ if (grub_memcmp (guid, &GRUB_PKS_CERT_X509_SHA256_GUID, GRUB_PACKED_GUID_SIZE) == 0 ||
+ grub_memcmp (guid, &GRUB_PKS_CERT_X509_SHA384_GUID, GRUB_PACKED_GUID_SIZE) == 0 ||
+ grub_memcmp (guid, &GRUB_PKS_CERT_X509_SHA512_GUID, GRUB_PACKED_GUID_SIZE) == 0)
+ return true;
+
+ return false;
+}
+
+static bool
+is_x509 (const grub_packed_guid_t *guid)
+{
+ if (grub_memcmp (guid, &GRUB_PKS_CERT_X509_GUID, GRUB_PACKED_GUID_SIZE) == 0)
+ return true;
+
+ return false;
+}
+
+static bool
+is_cert_match (const grub_x509_cert_t *cert1, const grub_x509_cert_t *cert2)
+{
+ if (grub_memcmp (cert1->subject, cert2->subject, cert2->subject_len) == 0
+ && grub_memcmp (cert1->issuer, cert2->issuer, cert2->issuer_len) == 0
+ && grub_memcmp (cert1->serial, cert2->serial, cert2->serial_len) == 0
+ && grub_memcmp (cert1->mpis[GRUB_RSA_PK_MODULUS], cert2->mpis[GRUB_RSA_PK_MODULUS],
+ sizeof (cert2->mpis[GRUB_RSA_PK_MODULUS])) == 0
+ && grub_memcmp (cert1->mpis[GRUB_RSA_PK_EXPONENT], cert2->mpis[GRUB_RSA_PK_EXPONENT],
+ sizeof (cert2->mpis[GRUB_RSA_PK_EXPONENT])) == 0
+ && grub_memcmp (cert1->fingerprint[GRUB_FINGERPRINT_SHA256],
+ cert2->fingerprint[GRUB_FINGERPRINT_SHA256],
+ grub_strlen ((char *) cert2->fingerprint[GRUB_FINGERPRINT_SHA256])) == 0)
+ return true;
+
+ return false;
+}
+
+/* Check the certificate hash presence in the dbx list. */
+static bool
+is_cert_hash_present_in_dbx (const grub_uint8_t *data, const grub_size_t data_size)
+{
+ grub_err_t rc;
+ grub_uint32_t i;
+ grub_size_t cert_hash_size = 0;
+ grub_uint8_t cert_hash[GRUB_MAX_HASH_LEN] = { 0 };
+
+ for (i = 0; i < dbx.hash_entries; i++)
+ {
+ if (dbx.hashes[i] == NULL)
+ continue;
+
+ rc = generate_cert_hash (dbx.hash_sizes[i], data, data_size, cert_hash, &cert_hash_size);
+ if (rc != GRUB_ERR_NONE)
+ continue;
+
+ if (cert_hash_size == dbx.hash_sizes[i] &&
+ grub_memcmp (dbx.hashes[i], cert_hash, cert_hash_size) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+/* Check the certificate presence in the db/dbx list. */
+static bool
+check_cert_presence (const grub_x509_cert_t *cert_in, const sb_database_t *sb_database)
+{
+ grub_x509_cert_t *cert;
+
+ for (cert = sb_database->certs; cert != NULL; cert = cert->next)
+ if (is_cert_match (cert, cert_in) == true)
+ return true;
+
+ return false;
+}
+
+/*
+ * Add the certificate into the db list if it is not present in the dbx and db
+ * list when is_db is true. Add the certificate into the dbx list when is_db is
+ * false.
+ */
+static grub_err_t
+add_certificate (const grub_uint8_t *data, const grub_size_t data_size,
+ sb_database_t *sb_database)
+{
+ grub_err_t rc;
+ grub_x509_cert_t *cert;
+
+ if (data == NULL || data_size == 0)
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, "certificate data or size is not available");
+
+ cert = grub_zalloc (sizeof (grub_x509_cert_t));
+ if (cert == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
+
+ rc = grub_x509_cert_parse (data, data_size, cert);
+ if (rc != GRUB_ERR_NONE)
+ {
+ grub_dprintf ("appendedsig", "cannot add a certificate CN='%s' to the %s list\n",
+ cert->subject, (sb_database->is_db == true) ? "db" : "dbx");
+ grub_free (cert);
+ return rc;
+ }
+
+ /*
+ * Only checks the certificate against dbx if is_db is true when dynamic key
+ * management is enabled.
+ */
+ if (append_key_mgmt == true)
+ {
+ if (sb_database->is_db == true)
+ {
+ if (is_cert_hash_present_in_dbx (data, data_size) == true ||
+ check_cert_presence (cert, &dbx) == true)
+ {
+ grub_dprintf ("appendedsig",
+ "cannot add a certificate CN='%s', as it is present in the dbx list",
+ cert->subject);
+ rc = GRUB_ERR_ACCESS_DENIED;
+ goto fail;
+ }
+ }
+ }
+
+ if (check_cert_presence (cert, sb_database) == true)
+ {
+ grub_dprintf ("appendedsig",
+ "cannot add a certificate CN='%s', as it is present in the %s list",
+ cert->subject, ((sb_database->is_db == true) ? "db" : "dbx"));
+ rc = GRUB_ERR_EXISTS;
+ goto fail;
+ }
+
+ grub_dprintf ("appendedsig", "added a certificate CN='%s' to the %s list\n",
+ cert->subject, ((sb_database->is_db == true) ? "db" : "dbx"));
+
+ cert->next = sb_database->certs;
+ sb_database->certs = cert;
+ sb_database->cert_entries++;
+
+ return rc;
+
+ fail:
+ grub_x509_cert_release (cert);
+ grub_free (cert);
+
+ return rc;
+}
+
+static void
+_remove_cert_from_db (const grub_x509_cert_t *cert)
+{
+ grub_uint32_t i = 1;
+ grub_x509_cert_t *curr_cert, *prev_cert;
+
+ for (curr_cert = prev_cert = db.certs; curr_cert != NULL; curr_cert = curr_cert->next, i++)
+ {
+ if (is_cert_match (curr_cert, cert) == true)
+ {
+ if (i == 1) /* Match with first certificate in the db list. */
+ db.certs = curr_cert->next;
+ else
+ prev_cert->next = curr_cert->next;
+
+ grub_dprintf ("appendedsig",
+ "removed distrusted certificate with CN: %s from the db list\n",
+ curr_cert->subject);
+ curr_cert->next = NULL;
+ grub_x509_cert_release (curr_cert);
+ grub_free (curr_cert);
+ break;
+ }
+ else
+ prev_cert = curr_cert;
+ }
+}
+
+static grub_err_t
+remove_cert_from_db (const grub_uint8_t *data, const grub_size_t data_size)
+{
+ grub_err_t rc;
+ grub_x509_cert_t *cert;
+
+ if (data == NULL || data_size == 0)
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, "certificate data or size is not available");
+
+ cert = grub_zalloc (sizeof (grub_x509_cert_t));
+ if (cert == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
+
+ rc = grub_x509_cert_parse (data, data_size, cert);
+ if (rc != GRUB_ERR_NONE)
+ {
+ grub_dprintf ("appendedsig", "cannot remove an invalid certificate from the db list\n");
+ grub_free (cert);
+ return rc;
+ }
+
+ /* Remove certificate from the db list. */
+ _remove_cert_from_db (cert);
+
+ return rc;
+}
+
+static bool
+cert_fingerprint_match (const grub_uint8_t *hash_data, const grub_size_t hash_data_size,
+ const grub_x509_cert_t *cert)
+{
+ grub_int32_t type;
+
+ if (hash_data_size == SHA256_HASH_SIZE)
+ type = GRUB_FINGERPRINT_SHA256;
+ else if (hash_data_size == SHA384_HASH_SIZE)
+ type = GRUB_FINGERPRINT_SHA384;
+ else if (hash_data_size == SHA512_HASH_SIZE)
+ type = GRUB_FINGERPRINT_SHA512;
+ else
+ {
+ grub_dprintf ("appendedsig", "unsupported fingerprint hash type "
+ "(%" PRIuGRUB_SIZE ") \n", hash_data_size);
+ return false;
+ }
+
+ if (grub_memcmp (cert->fingerprint[type], hash_data, hash_data_size) == 0)
+ return true;
+
+ return false;
+}
+
+static void
+remove_hash_from_db (const grub_uint8_t *hash_data, const grub_size_t hash_data_size,
+ const bool bin_hash)
+{
+ grub_uint32_t i;
+ grub_x509_cert_t *cert;
+
+ if (bin_hash == true)
+ {
+ for (i = 0; i < db.hash_entries; i++)
+ {
+ if (db.hashes[i] == NULL)
+ continue;
+
+ if (grub_memcmp (db.hashes[i], hash_data, hash_data_size) == 0)
+ {
+ grub_dprintf ("appendedsig", "removed distrusted hash %02x%02x%02x%02x.. from the db list\n",
+ db.hashes[i][0], db.hashes[i][1], db.hashes[i][2], db.hashes[i][3]);
+ grub_free (db.hashes[i]);
+ db.hashes[i] = NULL;
+ db.hash_sizes[i] = 0;
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (cert = db.certs; cert != NULL; cert = cert->next)
+ {
+ if (cert_fingerprint_match (hash_data, hash_data_size, cert) == true)
+ {
+ _remove_cert_from_db (cert);
+ break;
+ }
+ }
+ }
+}
+
+static grub_err_t
+file_read_whole (grub_file_t file, grub_uint8_t **buf, grub_size_t *len)
+{
+ grub_off_t full_file_size;
+ grub_size_t file_size, total_read_size = 0;
+ grub_ssize_t read_size;
+
+ full_file_size = grub_file_size (file);
+ if (full_file_size == GRUB_FILE_SIZE_UNKNOWN)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "cannot read a file of unknown size into a buffer");
+
+ if (full_file_size > GRUB_SIZE_MAX)
+ return grub_error (GRUB_ERR_OUT_OF_RANGE,
+ "file is too large to read: %" PRIuGRUB_OFFSET " bytes",
+ full_file_size);
+
+ file_size = (grub_size_t) full_file_size;
+ *buf = grub_malloc (file_size);
+ if (*buf == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not allocate file data buffer size %" PRIuGRUB_SIZE,
+ file_size);
+
+ while (total_read_size < file_size)
+ {
+ read_size = grub_file_read (file, *buf + total_read_size, file_size - total_read_size);
+ if (read_size < 0)
+ {
+ grub_free (*buf);
+ return grub_errno;
+ }
+ else if (read_size == 0)
+ {
+ grub_free (*buf);
+ return grub_error (GRUB_ERR_IO,
+ "could not read full file size "
+ "(%" PRIuGRUB_SIZE "), only %" PRIuGRUB_SIZE " bytes read",
+ file_size, total_read_size);
+ }
+
+ total_read_size += read_size;
+ }
+
+ *len = file_size;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+extract_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize,
+ sb_appendedsig_t *sig)
+{
+ grub_size_t appendedsig_pkcs7_size;
+ grub_size_t signed_data_size = bufsize;
+ const grub_uint8_t *signed_data = buf;
+
+ if (signed_data_size < SIG_MAGIC_SIZE)
+ return grub_error (GRUB_ERR_BAD_SIGNATURE, "file too short for signature magic");
+
+ /* Fast-forwarding pointer and get signature magic string. */
+ signed_data += signed_data_size - SIG_MAGIC_SIZE;
+ if (grub_strncmp ((const char *) signed_data, SIG_MAGIC, SIG_MAGIC_SIZE))
+ return grub_error (GRUB_ERR_BAD_SIGNATURE, "missing or invalid signature magic");
+
+ signed_data_size -= SIG_MAGIC_SIZE;
+ if (signed_data_size < SIG_METADATA_SIZE)
+ return grub_error (GRUB_ERR_BAD_SIGNATURE, "file too short for signature metadata");
+
+ /* Rewind pointer and extract signature metadata. */
+ signed_data -= SIG_METADATA_SIZE;
+ grub_memcpy (&(sig->sig_metadata), signed_data, SIG_METADATA_SIZE);
+
+ if (sig->sig_metadata.id_type != PKEY_ID_PKCS7)
+ return grub_error (GRUB_ERR_BAD_SIGNATURE, "wrong signature type");
+
+ appendedsig_pkcs7_size = grub_be_to_cpu32 (sig->sig_metadata.sig_len);
+
+ signed_data_size -= SIG_METADATA_SIZE;
+ if (appendedsig_pkcs7_size > signed_data_size)
+ return grub_error (GRUB_ERR_BAD_SIGNATURE, "file too short for PKCS#7 message");
+
+ grub_dprintf ("appendedsig", "sig len %" PRIuGRUB_SIZE "\n", appendedsig_pkcs7_size);
+
+ /* Appended signature size. */
+ sig->signature_len = APPENDED_SIG_SIZE (appendedsig_pkcs7_size);
+ /* Rewind pointer and parse appended pkcs7 data. */
+ signed_data -= appendedsig_pkcs7_size;
+
+ return grub_pkcs7_data_parse (signed_data, appendedsig_pkcs7_size, &sig->pkcs7);
+}
+
+static grub_err_t
+get_binary_hash (const grub_size_t binary_hash_size, const grub_uint8_t *data,
+ const grub_size_t data_size, grub_uint8_t *hash, grub_size_t *hash_size)
+{
+ grub_packed_guid_t guid = { 0 };
+
+ /* support SHA256, SHA384 and SHA512 for binary hash */
+ if (binary_hash_size == SHA256_HASH_SIZE)
+ grub_memcpy (&guid, &GRUB_PKS_CERT_SHA256_GUID, GRUB_PACKED_GUID_SIZE);
+ else if (binary_hash_size == SHA384_HASH_SIZE)
+ grub_memcpy (&guid, &GRUB_PKS_CERT_SHA384_GUID, GRUB_PACKED_GUID_SIZE);
+ else if (binary_hash_size == SHA512_HASH_SIZE)
+ grub_memcpy (&guid, &GRUB_PKS_CERT_SHA512_GUID, GRUB_PACKED_GUID_SIZE);
+ else
+ {
+ grub_dprintf ("appendedsig", "unsupported hash type (%" PRIuGRUB_SIZE ") and "
+ "skipped\n", binary_hash_size);
+ return GRUB_ERR_UNKNOWN_COMMAND;
+ }
+
+ return get_hash (&guid, data, data_size, hash, hash_size);
+}
+
+/*
+ * Verify binary hash against the db and dbx list.
+ * The following errors can occur:
+ * - GRUB_ERR_BAD_SIGNATURE: indicates that the hash is in dbx list.
+ * - GRUB_ERR_EOF: the hash could not be found in the db and dbx list.
+ * - GRUB_ERR_NONE: the hash is found in db list.
+ */
+static grub_err_t
+verify_binary_hash (const grub_uint8_t *data, const grub_size_t data_size)
+{
+ grub_err_t rc = GRUB_ERR_NONE;
+ grub_uint32_t i;
+ grub_size_t hash_size = 0;
+ grub_uint8_t hash[GRUB_MAX_HASH_LEN] = { 0 };
+
+ for (i = 0; i < dbx.hash_entries; i++)
+ {
+ if (dbx.hashes[i] == NULL)
+ continue;
+
+ rc = get_binary_hash (dbx.hash_sizes[i], data, data_size, hash, &hash_size);
+ if (rc != GRUB_ERR_NONE)
+ continue;
+
+ if (hash_size == dbx.hash_sizes[i] &&
+ grub_memcmp (dbx.hashes[i], hash, hash_size) == 0)
+ {
+ grub_dprintf ("appendedsig", "the hash (%02x%02x%02x%02x) is present in the dbx list\n",
+ hash[0], hash[1], hash[2], hash[3]);
+ return GRUB_ERR_BAD_SIGNATURE;
+ }
+ }
+
+ for (i = 0; i < db.hash_entries; i++)
+ {
+ if (db.hashes[i] == NULL)
+ continue;
+
+ rc = get_binary_hash (db.hash_sizes[i], data, data_size, hash, &hash_size);
+ if (rc != GRUB_ERR_NONE)
+ continue;
+
+ if (hash_size == db.hash_sizes[i] &&
+ grub_memcmp (db.hashes[i], hash, hash_size) == 0)
+ {
+ grub_dprintf ("appendedsig", "verified with a trusted hash (%02x%02x%02x%02x)\n",
+ hash[0], hash[1], hash[2], hash[3]);
+ return GRUB_ERR_NONE;
+ }
+ }
+
+ return GRUB_ERR_EOF;
+}
+
+/*
+ * Given a hash value 'hval', of hash specification 'hash', prepare the
+ * S-expressions (sexp) and perform the signature verification.
+ */
+static grub_err_t
+verify_signature (const gcry_mpi_t *pkmpi, const gcry_mpi_t hmpi,
+ const gcry_md_spec_t *hash, const grub_uint8_t *hval)
+{
+ gcry_sexp_t hsexp, pubkey, sig;
+ grub_size_t errof;
+
+ if (_gcry_sexp_build (&hsexp, &errof, "(data (flags %s) (hash %s %b))", "pkcs1",
+ hash->name, hash->mdlen, hval) != GPG_ERR_NO_ERROR)
+ return GRUB_ERR_BAD_SIGNATURE;
+
+ if (_gcry_sexp_build (&pubkey, &errof, "(public-key (dsa (n %M) (e %M)))",
+ pkmpi[0], pkmpi[1]) != GPG_ERR_NO_ERROR)
+ return GRUB_ERR_BAD_SIGNATURE;
+
+ if (_gcry_sexp_build (&sig, &errof, "(sig-val (rsa (s %M)))", hmpi) != GPG_ERR_NO_ERROR)
+ return GRUB_ERR_BAD_SIGNATURE;
+
+ _gcry_sexp_dump (sig);
+ _gcry_sexp_dump (hsexp);
+ _gcry_sexp_dump (pubkey);
+
+ if (grub_crypto_pk_rsa->verify (sig, hsexp, pubkey) != GPG_ERR_NO_ERROR)
+ return GRUB_ERR_BAD_SIGNATURE;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize)
+{
+ grub_err_t err;
+ grub_size_t datasize;
+ void *context;
+ grub_uint8_t *hash;
+ grub_x509_cert_t *pk;
+ sb_appendedsig_t sig;
+ grub_pkcs7_signer_t *si;
+ grub_int32_t i;
+
+ if (!db.cert_entries && !db.hash_entries)
+ return grub_error (GRUB_ERR_BAD_SIGNATURE, "no trusted keys to verify against");
+
+ err = extract_appended_signature (buf, bufsize, &sig);
+ if (err != GRUB_ERR_NONE)
+ return err;
+
+ append_sig_len = sig.signature_len;
+ datasize = bufsize - sig.signature_len;
+
+ /*
+ * If signature verification is enabled with dynamic key management mode,
+ * Verify binary hash against the db and dbx list.
+ */
+ if (append_key_mgmt == true)
+ {
+ err = verify_binary_hash (buf, datasize);
+ if (err == GRUB_ERR_BAD_SIGNATURE)
+ {
+ grub_pkcs7_data_release (&sig.pkcs7);
+ return grub_error (err,
+ "failed to verify the binary hash against a trusted binary hash");
+ }
+ }
+
+ /* Verify signature using trusted keys from db list. */
+ for (i = 0; i < sig.pkcs7.signer_count; i++)
+ {
+ si = &sig.pkcs7.signers[i];
+ context = grub_zalloc (si->hash->contextsize);
+ if (context == NULL)
+ return grub_errno;
+
+ si->hash->init (context, 0);
+ si->hash->write (context, buf, datasize);
+ si->hash->final (context);
+ hash = si->hash->read (context);
+
+ grub_dprintf ("appendedsig", "data size %" PRIuGRUB_SIZE ", signer %d hash %02x%02x%02x%02x...\n",
+ datasize, i, hash[0], hash[1], hash[2], hash[3]);
+
+ for (pk = db.certs; pk != NULL; pk = pk->next)
+ {
+ err = verify_signature (pk->mpis, si->sig_mpi, si->hash, hash);
+ if (err == GRUB_ERR_NONE)
+ {
+ grub_dprintf ("appendedsig", "verify signer %d with key '%s' succeeded\n",
+ i, pk->subject);
+ break;
+ }
+
+ grub_dprintf ("appendedsig", "verify signer %d with key '%s' failed\n",
+ i, pk->subject);
+ }
+
+ grub_free (context);
+ if (err == GRUB_ERR_NONE)
+ break;
+ }
+
+ grub_pkcs7_data_release (&sig.pkcs7);
+
+ if (err != GRUB_ERR_NONE)
+ return grub_error (err, "failed to verify signature against a trusted key");
+
+ return err;
+}
+
+static grub_err_t
+grub_cmd_verify_signature (grub_command_t cmd __attribute__ ((unused)), int argc, char **args)
+{
+ grub_file_t signed_file;
+ grub_err_t err;
+ grub_uint8_t *signed_data = NULL;
+ grub_size_t signed_data_size = 0;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "a signed file is expected\nExample:\n\tappend_verify \n");
+
+ if (!grub_strlen (args[0]))
+ return grub_error (GRUB_ERR_BAD_FILENAME, "missing signed file");
+
+ grub_dprintf ("appendedsig", "verifying %s\n", args[0]);
+
+ signed_file = grub_file_open (args[0], GRUB_FILE_TYPE_VERIFY_SIGNATURE);
+ if (signed_file == NULL)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND, "could not open %s file", args[0]);
+
+ err = file_read_whole (signed_file, &signed_data, &signed_data_size);
+ if (err == GRUB_ERR_NONE)
+ {
+ err = grub_verify_appended_signature (signed_data, signed_data_size);
+ grub_free (signed_data);
+ }
+
+ grub_file_close (signed_file);
+
+ return err;
+}
+
+/*
+ * Checks the trusted certificate against dbx list if dynamic key management is
+ * enabled. And add it to the db list if it is not already present.
+ *
+ * Note: When signature verification is enabled, this command only accepts the
+ * trusted certificate that is signed with an appended signature.
+ * The signature is verified by the appendedsig module. If verification succeeds,
+ * the certificate is added to the db list. Otherwise, an error is posted and
+ * the certificate is not added.
+ * When signature verification is disabled, it accepts the trusted certificate
+ * without an appended signature and add it to the db list.
+ *
+ * Also, note that the adding of the trusted certificate using this command does
+ * not persist across reboots.
+ */
+static grub_err_t
+grub_cmd_db_cert (grub_command_t cmd __attribute__ ((unused)), int argc, char **args)
+{
+ grub_err_t err;
+ grub_file_t cert_file;
+ grub_uint8_t *cert_data = NULL;
+ grub_size_t cert_data_size = 0;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "a trusted X.509 certificate file is expected in DER format\n"
+ "Example:\n\tappend_add_db_cert \n");
+
+ if (!grub_strlen (args[0]))
+ return grub_error (GRUB_ERR_BAD_FILENAME, "missing trusted X.509 certificate file");
+
+ cert_file = grub_file_open (args[0],
+ GRUB_FILE_TYPE_CERTIFICATE_TRUST | GRUB_FILE_TYPE_NO_DECOMPRESS);
+ if (cert_file == NULL)
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "could not open %s file", args[0]);
+
+ err = file_read_whole (cert_file, &cert_data, &cert_data_size);
+ grub_file_close (cert_file);
+ if (err != GRUB_ERR_NONE)
+ return err;
+
+ /*
+ * If signature verification is enabled (check_sigs is set to true), obtain
+ * the actual certificate size by subtracting the appended signature size from
+ * the certificate size because the certificate has an appended signature, and
+ * this actual certificate size is used to get the X.509 certificate.
+ */
+ if (check_sigs == true)
+ cert_data_size -= append_sig_len;
+
+ err = add_certificate (cert_data, cert_data_size, &db);
+ grub_free (cert_data);
+
+ return err;
+}
+
+/*
+ * Remove the distrusted certificate from the db list if it is already present.
+ * And add it to the dbx list if not present when dynamic key management is
+ * enabled.
+ *
+ * Note: When signature verification is enabled, this command only accepts the
+ * distrusted certificate that is signed with an appended signature.
+ * The signature is verified by the appended sig module. If verification
+ * succeeds, the certificate is removed from the db list. Otherwise, an error
+ * is posted and the certificate is not removed.
+ * When signature verification is disabled, it accepts the distrusted certificate
+ * without an appended signature and removes it from the db list.
+ *
+ * Also, note that the removal of the distrusted certificate using this command
+ * does not persist across reboots.
+ */
+static grub_err_t
+grub_cmd_dbx_cert (grub_command_t cmd __attribute__ ((unused)), int argc, char **args)
+{
+ grub_err_t err;
+ grub_file_t cert_file;
+ grub_uint8_t *cert_data = NULL;
+ grub_size_t cert_data_size = 0;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "a distrusted X.509 certificate file is expected in DER format\n"
+ "Example:\n\tappend_add_dbx_cert \n");
+
+ if (!grub_strlen (args[0]))
+ return grub_error (GRUB_ERR_BAD_FILENAME, "missing distrusted X.509 certificate file");
+
+ cert_file = grub_file_open (args[0],
+ GRUB_FILE_TYPE_CERTIFICATE_TRUST | GRUB_FILE_TYPE_NO_DECOMPRESS);
+ if (cert_file == NULL)
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "could not open %s file", args[0]);
+
+ err = file_read_whole (cert_file, &cert_data, &cert_data_size);
+ grub_file_close (cert_file);
+ if (err != GRUB_ERR_NONE)
+ return err;
+
+ /*
+ * If signature verification is enabled (check_sigs is set to true), obtain
+ * the actual certificate size by subtracting the appended signature size from
+ * the certificate size because the certificate has an appended signature, and
+ * this actual certificate size is used to get the X.509 certificate.
+ */
+ if (check_sigs == true)
+ cert_data_size -= append_sig_len;
+
+ /* Remove distrusted certificate from the db list if present. */
+ err = remove_cert_from_db (cert_data, cert_data_size);
+ if (err != GRUB_ERR_NONE)
+ {
+ grub_free (cert_data);
+ return err;
+ }
+
+ /* Only add the certificate to the dbx list if dynamic key management is enabled. */
+ if (append_key_mgmt == true)
+ err = add_certificate (cert_data, cert_data_size, &dbx);
+
+ grub_free (cert_data);
+
+ return err;
+}
+
+static grub_err_t
+grub_cmd_list_db (grub_command_t cmd __attribute__ ((unused)), int argc __attribute__ ((unused)),
+ char **args __attribute__ ((unused)))
+{
+ struct x509_certificate *cert;
+ grub_uint32_t i, cert_num = 1;
+
+ for (cert = db.certs; cert != NULL; cert = cert->next, cert_num++)
+ print_certificate (cert, cert_num);
+
+ if (append_key_mgmt == false)
+ return GRUB_ERR_NONE;
+
+ for (i = 0; i < db.hash_entries; i++)
+ {
+ if (db.hashes[i] != NULL)
+ {
+ grub_printf ("\nBinary hash: %u\n", i + 1);
+ grub_printf (" Hash: sha%" PRIuGRUB_SIZE "\n ", db.hash_sizes[i] * 8);
+ hexdump_colon (db.hashes[i], db.hash_sizes[i]);
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_list_dbx (grub_command_t cmd __attribute__((unused)),
+ int argc __attribute__((unused)), char **args __attribute__((unused)))
+{
+ struct x509_certificate *cert;
+ grub_uint32_t i, cert_num = 1;
+
+ if (append_key_mgmt == false)
+ return grub_error (GRUB_ERR_ACCESS_DENIED,
+ "append_list_dbx command is unsupported in static key mode");
+
+ for (cert = dbx.certs; cert != NULL; cert = cert->next, cert_num++)
+ print_certificate (cert, cert_num);
+
+ for (i = 0; i < dbx.hash_entries; i++)
+ {
+ if (dbx.hashes[i] != NULL)
+ {
+ grub_printf ("\nCertificate/Binary hash: %u\n", i + 1);
+ grub_printf (" Hash: sha%" PRIuGRUB_SIZE "\n ", dbx.hash_sizes[i] * 8);
+ hexdump_colon (dbx.hashes[i], dbx.hash_sizes[i]);
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+/*
+ * Remove the trusted binary hash from the dbx list if present. And add them to
+ * the db list if it is not already present.
+ *
+ * Note: When signature verification is enabled, this command only accepts the
+ * binary hash file that is signed with an appended signature. The signature is
+ * verified by the appendedsig module. If verification succeeds, the binary hash
+ * is added to the db list. Otherwise, an error is posted and the binary hash is
+ * not added.
+ * When signature verification is disabled, it accepts the binary hash file
+ * without an appended signature and adds it to the db list.
+ *
+ * Also, note that the adding of the trusted binary hash using this command does
+ * not persist across reboots.
+ */
+static grub_err_t
+grub_cmd_add_db_hash (grub_command_t cmd __attribute__((unused)), int argc, char**args)
+{
+ grub_err_t rc;
+ grub_file_t hash_file;
+ grub_uint8_t *hash_data = NULL;
+ grub_size_t hash_data_size = 0;
+
+ if (append_key_mgmt == false)
+ return grub_error (GRUB_ERR_ACCESS_DENIED,
+ "append_add_db_hash command is unsupported in static key mode");
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "a trusted binary hash file is expected in binary format\n"
+ "Example:\n\tappend_add_db_hash \n");
+
+ if (!grub_strlen (args[0]))
+ return grub_error (GRUB_ERR_BAD_FILENAME, "missing trusted binary hash file");
+
+ hash_file = grub_file_open (args[0], GRUB_FILE_TYPE_HASH_TRUST | GRUB_FILE_TYPE_NO_DECOMPRESS);
+ if (hash_file == NULL)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND, "unable to open %s file", args[0]);
+
+ rc = file_read_whole (hash_file, &hash_data, &hash_data_size);
+ grub_file_close (hash_file);
+ if (rc != GRUB_ERR_NONE)
+ return rc;
+
+ /*
+ * If signature verification is enabled (check_sigs is set to true), obtain
+ * the actual hash data size by subtracting the appended signature size from
+ * the hash data size because the hash has an appended signature, and this
+ * actual hash data size is used to get the hash data.
+ */
+ if (check_sigs == true)
+ hash_data_size -= append_sig_len;
+
+ grub_dprintf ("appendedsig",
+ "adding a trusted binary hash %02x%02x%02x%02x... with size of %" PRIuGRUB_SIZE "\n",
+ hash_data[0], hash_data[1], hash_data[2], hash_data[3], hash_data_size);
+
+ /* Only accept SHA256, SHA384 and SHA512 binary hash */
+ if (hash_data_size != SHA256_HASH_SIZE && hash_data_size != SHA384_HASH_SIZE &&
+ hash_data_size != SHA512_HASH_SIZE)
+ {
+ grub_free (hash_data);
+ return grub_error (GRUB_ERR_BAD_SIGNATURE, "unacceptable trusted binary hash type");
+ }
+
+ rc = add_hash (hash_data, hash_data_size, &db);
+ grub_free (hash_data);
+
+ return rc;
+}
+
+/*
+ * Remove the distrusted binary/certificate hash from the db list if present.
+ * And add them to the dbx list if it is not already present.
+ *
+ * Note: When signature verification is enabled, this command only accepts the
+ * binary/certificate hash file that is signed with an appended signature. The
+ * signature is verified by the appendedsig module. If verification succeeds,
+ * the binary/certificate hash is added to the dbx list. Otherwise, an error is
+ * posted and the binary/certificate hash is not added.
+ * When signature verification is disabled, it accepts the binary/certificate
+ * hash file without an appended signature and adds it to the dbx list.
+ *
+ * Also, note that the adding of the distrusted binary/certificate hash using
+ * this command does not persist across reboots.
+ */
+static grub_err_t
+grub_cmd_add_dbx_hash (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)),
+ char **args __attribute__ ((unused)))
+{
+ grub_err_t rc;
+ grub_file_t hash_file;
+ grub_uint8_t *hash_data = NULL;
+ grub_size_t hash_data_size = 0;
+ char *file_path;
+
+ if (append_key_mgmt == false)
+ return grub_error (GRUB_ERR_ACCESS_DENIED,
+ "append_add_dbx_hash command is unsupported in static key mode");
+
+ if (!ctxt->state[OPTION_BINARY_HASH].set && !ctxt->state[OPTION_CERT_HASH].set)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "a distrusted certificate/binary hash file is expected in binary format\n"
+ "Example:\n\tappend_add_dbx_hash [option] \n"
+ "option:\n[-b|--binary-hash] FILE [BINARY HASH FILE]\n"
+ "[-c|--cert-hash] FILE [CERTFICATE HASH FILE]\n");
+
+ if (ctxt->state[OPTION_BINARY_HASH].arg == NULL && ctxt->state[OPTION_CERT_HASH].arg == NULL)
+ return grub_error (GRUB_ERR_BAD_FILENAME, "missing distrusted certificate/binary hash file");
+
+ if (ctxt->state[OPTION_BINARY_HASH].arg != NULL)
+ file_path = ctxt->state[OPTION_BINARY_HASH].arg;
+ else
+ file_path = ctxt->state[OPTION_CERT_HASH].arg;
+
+ hash_file = grub_file_open (file_path, GRUB_FILE_TYPE_HASH_TRUST | GRUB_FILE_TYPE_NO_DECOMPRESS);
+ if (hash_file == NULL)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND, "unable to open %s file", file_path);
+
+ rc = file_read_whole (hash_file, &hash_data, &hash_data_size);
+ grub_file_close (hash_file);
+ if (rc != GRUB_ERR_NONE)
+ return rc;
+
+ /*
+ * If signature verification is enabled (check_sigs is set to true), obtain
+ * the actual hash data size by subtracting the appended signature size from
+ * the hash data size because the hash has an appended signature, and this
+ * actual hash data size is used to get the hash data.
+ */
+ if (check_sigs == true)
+ hash_data_size -= append_sig_len;
+
+ grub_dprintf ("appendedsig",
+ "adding a distrusted certificate/binary hash %02x%02x%02x%02x..."
+ " with size of %" PRIuGRUB_SIZE "\n", hash_data[0], hash_data[1],
+ hash_data[2], hash_data[3], hash_data_size);
+
+ if (ctxt->state[OPTION_BINARY_HASH].set || ctxt->state[OPTION_CERT_HASH].set)
+ {
+ /* Only accept SHA256, SHA384 and SHA512 certificate/binary hash */
+ if (hash_data_size != SHA256_HASH_SIZE && hash_data_size != SHA384_HASH_SIZE &&
+ hash_data_size != SHA512_HASH_SIZE)
+ {
+ grub_free (hash_data);
+ return grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "unacceptable distrusted certificate/binary hash type");
+ }
+ }
+
+ /* Remove distrusted binary hash/certificate from the db list if present. */
+ remove_hash_from_db (hash_data, hash_data_size,
+ (ctxt->state[OPTION_BINARY_HASH].set) ? true : false);
+ rc = add_hash (hash_data, hash_data_size, &dbx);
+ grub_free (hash_data);
+
+ return rc;
+}
+
+/* Add the X.509 certificates/binary hash to the db list from PKS. */
+static grub_err_t
+load_pks2db (void)
+{
+ grub_err_t rc;
+ grub_uint32_t i;
+
+ for (i = 0; i < pks_keystore->db_entries; i++)
+ {
+ if (is_hash (&pks_keystore->db[i].guid) == true)
+ {
+ rc = add_hash (pks_keystore->db[i].data,
+ pks_keystore->db[i].data_size, &db);
+ if (rc == GRUB_ERR_OUT_OF_MEMORY)
+ return rc;
+ }
+ else if (is_x509 (&pks_keystore->db[i].guid) == true)
+ {
+ rc = add_certificate (pks_keystore->db[i].data,
+ pks_keystore->db[i].data_size, &db);
+ if (rc == GRUB_ERR_OUT_OF_MEMORY)
+ return rc;
+ }
+ else
+ grub_dprintf ("appendedsig", "unsupported signature data type and "
+ "skipped (%u)\n", i + 1);
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+/* Add the certificates and certificate/binary hash to the dbx list from PKS. */
+static grub_err_t
+load_pks2dbx (void)
+{
+ grub_err_t rc;
+ grub_uint32_t i;
+
+ for (i = 0; i < pks_keystore->dbx_entries; i++)
+ {
+ if (is_x509 (&pks_keystore->dbx[i].guid) == true)
+ {
+ rc = add_certificate (pks_keystore->dbx[i].data,
+ pks_keystore->dbx[i].data_size, &dbx);
+ if (rc == GRUB_ERR_OUT_OF_MEMORY)
+ return rc;
+ }
+ else if (is_hash (&pks_keystore->dbx[i].guid) == true)
+ {
+ rc = add_hash (pks_keystore->dbx[i].data,
+ pks_keystore->dbx[i].data_size, &dbx);
+ if (rc != GRUB_ERR_NONE)
+ return rc;
+ }
+ else
+ grub_dprintf ("appendedsig", "unsupported signature data type and "
+ "skipped (%u)\n", i + 1);
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+/*
+ * Extract the X.509 certificates from the ELF Note header, parse it, and add
+ * it to the db list.
+ */
+static void
+load_elf2db (void)
+{
+ grub_err_t err;
+ struct grub_module_header *header;
+ struct grub_file pseudo_file;
+ grub_uint8_t *cert_data = NULL;
+ grub_size_t cert_data_size = 0;
+
+ FOR_MODULES (header)
+ {
+ /* Not an X.509 certificate, skip. */
+ if (header->type != OBJ_TYPE_X509_PUBKEY)
+ continue;
+
+ grub_memset (&pseudo_file, 0, sizeof (pseudo_file));
+ pseudo_file.fs = &pseudo_fs;
+ pseudo_file.size = header->size - sizeof (struct grub_module_header);
+ pseudo_file.data = (char *) header + sizeof (struct grub_module_header);
+
+ grub_dprintf ("appendedsig", "found an X.509 certificate, size=%" PRIuGRUB_UINT64_T "\n",
+ pseudo_file.size);
+
+ err = file_read_whole (&pseudo_file, &cert_data, &cert_data_size);
+ if (err == GRUB_ERR_OUT_OF_MEMORY)
+ return;
+ else if (err != GRUB_ERR_NONE)
+ continue;
+
+ err = add_certificate (cert_data, cert_data_size, &db);
+ grub_free (cert_data);
+ if (err == GRUB_ERR_OUT_OF_MEMORY)
+ return;
+ }
+}
+
+/*
+ * Extract trusted and distrusted keys from PKS and store them in the db and
+ * dbx list.
+ */
+static void
+create_dbs_from_pks (void)
+{
+ grub_err_t err;
+
+ err = load_pks2dbx ();
+ if (err != GRUB_ERR_NONE)
+ grub_printf ("warning: dbx list might not be fully populated\n");
+
+ /*
+ * If db does not exist in the PKS storage, then read the static keys as a db
+ * default keys from the GRUB ELF Note and add them into the db list.
+ */
+ if (pks_keystore->db_exists == false)
+ load_elf2db ();
+ else
+ {
+ err = load_pks2db ();
+ if (err != GRUB_ERR_NONE)
+ grub_printf ("warning: db list might not be fully populated\n");
+ }
+
+ grub_pks_free_data ();
+ grub_dprintf ("appendedsig", "the db list now has %u keys\n"
+ "the dbx list now has %u keys\n",
+ db.hash_entries + db.cert_entries,
+ dbx.hash_entries + dbx.cert_entries);
+}
+
+/* Free db list memory */
+static void
+free_db_list (void)
+{
+ grub_x509_cert_t *cert;
+ grub_uint32_t i;
+
+ while (db.certs != NULL)
+ {
+ cert = db.certs;
+ db.certs = db.certs->next;
+ grub_x509_cert_release (cert);
+ grub_free (cert);
+ }
+
+ for (i = 0; i < db.hash_entries; i++)
+ grub_free (db.hashes[i]);
+
+ grub_free (db.hashes);
+ grub_free (db.hash_sizes);
+ grub_memset (&db, 0, sizeof (sb_database_t));
+}
+
+/* Free dbx list memory */
+static void
+free_dbx_list (void)
+{
+ grub_x509_cert_t *cert;
+ grub_uint32_t i;
+
+ while (dbx.certs != NULL)
+ {
+ cert = dbx.certs;
+ dbx.certs = dbx.certs->next;
+ grub_x509_cert_release (cert);
+ grub_free (cert);
+ }
+
+ for (i = 0; i < dbx.hash_entries; i++)
+ grub_free (dbx.hashes[i]);
+
+ grub_free (dbx.hashes);
+ grub_free (dbx.hash_sizes);
+ grub_memset (&dbx, 0, sizeof (sb_database_t));
+}
+
+static const char *
+grub_env_read_sec (struct grub_env_var *var __attribute__ ((unused)),
+ const char *val __attribute__ ((unused)))
+{
+ if (check_sigs == true)
+ return "yes";
+
+ return "no";
+}
+
+static char *
+grub_env_write_sec (struct grub_env_var *var __attribute__ ((unused)), const char *val)
+{
+ char *ret;
+
+ /*
+ * Do not allow the value to be changed if signature verification is enabled
+ * (check_sigs is set to true) and GRUB is locked down.
+ */
+ if (check_sigs == true && grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED)
+ {
+ ret = grub_strdup ("yes");
+ if (ret == NULL)
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "could not duplicate a string enforce");
+
+ return ret;
+ }
+
+ if (grub_strcmp (val, "yes") == 0)
+ check_sigs = true;
+ else if (grub_strcmp (val, "no") == 0)
+ check_sigs = false;
+
+ ret = grub_strdup (grub_env_read_sec (NULL, NULL));
+ if (ret == NULL)
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "could not duplicate a string %s",
+ grub_env_read_sec (NULL, NULL));
+
+ return ret;
+}
+
+static const char *
+grub_env_read_key_mgmt (struct grub_env_var *var __attribute__ ((unused)),
+ const char *val __attribute__ ((unused)))
+{
+ if (append_key_mgmt == true)
+ return "dynamic";
+
+ return "static";
+}
+
+static char *
+grub_env_write_key_mgmt (struct grub_env_var *var __attribute__ ((unused)), const char *val)
+{
+ char *ret;
+
+ /*
+ * Do not allow the value to be changed if signature verification is enabled
+ * (check_sigs is set to true) and GRUB is locked down.
+ */
+ if (check_sigs == true && grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED)
+ {
+ ret = grub_strdup (grub_env_read_key_mgmt (NULL, NULL));
+ if (ret == NULL)
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
+
+ return ret;
+ }
+
+ if (grub_strcmp (val, "dynamic") == 0)
+ append_key_mgmt = true;
+ else if (grub_strcmp (val, "static") == 0)
+ append_key_mgmt = false;
+
+ ret = grub_strdup (grub_env_read_key_mgmt (NULL, NULL));
+ if (ret == NULL)
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
+
+ return ret;
+}
+
+static grub_err_t
+appendedsig_init (grub_file_t io __attribute__ ((unused)), enum grub_file_type type,
+ void **context __attribute__ ((unused)), enum grub_verify_flags *flags)
+{
+ if (check_sigs == false)
+ {
+ *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
+ return GRUB_ERR_NONE;
+ }
+
+ switch (type & GRUB_FILE_TYPE_MASK)
+ {
+ case GRUB_FILE_TYPE_CERTIFICATE_TRUST:
+ /*
+ * This is a certificate to add to trusted keychain.
+ *
+ * This needs to be verified or blocked. Ideally we'd write an x509
+ * verifier, but we lack the hubris required to take this on. Instead,
+ * require that it have an appended signature.
+ */
+ case GRUB_FILE_TYPE_HASH_TRUST:
+ /*
+ * This is a certificate/binary hash to add to db/dbx. This needs to be
+ * verified or blocked.
+ */
+ case GRUB_FILE_TYPE_LINUX_KERNEL:
+ case GRUB_FILE_TYPE_GRUB_MODULE:
+ /*
+ * Appended signatures are only defined for ELF binaries. Out of an
+ * abundance of caution, we only verify Linux kernels and GRUB modules
+ * at this point.
+ */
+ *flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK;
+ return GRUB_ERR_NONE;
+
+ case GRUB_FILE_TYPE_ACPI_TABLE:
+ case GRUB_FILE_TYPE_DEVICE_TREE_IMAGE:
+ /*
+ * It is possible to use appended signature verification without
+ * lockdown - like the PGP verifier. When combined with an embedded
+ * config file in a signed GRUB binary, this could still be a meaningful
+ * secure-boot chain - so long as it isn't subverted by something like a
+ * rouge ACPI table or DT image. Defer them explicitly.
+ */
+ *flags = GRUB_VERIFY_FLAGS_DEFER_AUTH;
+ return GRUB_ERR_NONE;
+
+ default:
+ *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
+ return GRUB_ERR_NONE;
+ }
+}
+
+static grub_err_t
+appendedsig_write (void *ctxt __attribute__ ((unused)), void *buf, grub_size_t size)
+{
+ return grub_verify_appended_signature (buf, size);
+}
+
+struct grub_file_verifier grub_appendedsig_verifier = {
+ .name = "appendedsig",
+ .init = appendedsig_init,
+ .write = appendedsig_write,
+};
+
+static grub_command_t cmd_verify, cmd_list_db, cmd_dbx_cert, cmd_db_cert;
+static grub_command_t cmd_list_dbx, cmd_db_hash;
+static grub_extcmd_t cmd_dbx_hash;
+
+GRUB_MOD_INIT (appendedsig)
+{
+ grub_int32_t rc;
+
+ /*
+ * If secure boot is enabled with enforce mode and GRUB is locked down, enable
+ * signature verification.
+ */
+ if (grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED)
+ check_sigs = true;
+
+ /* If PKS keystore is available, use dynamic key management. */
+ pks_keystore = grub_pks_get_keystore ();
+ if (pks_keystore != NULL)
+ append_key_mgmt = true;
+
+ /*
+ * This is appended signature verification environment variable. It is
+ * automatically set to either "no" or "yes" based on the ’ibm,secure-boot’
+ * device tree property.
+ *
+ * "no": No signature verification. This is the default.
+ *
+ * "yes": Enforce signature verification. When GRUB is locked down, user cannot
+ * change the value by setting the check_appended_signatures variable
+ * back to ‘no’
+ */
+ grub_register_variable_hook ("check_appended_signatures", grub_env_read_sec, grub_env_write_sec);
+ grub_env_export ("check_appended_signatures");
+
+ /*
+ * This is appended signature key management environment variable. It is
+ * automatically set to either "static" or "dynamic" based on the
+ * Platform KeyStore.
+ *
+ * "static": Enforce static key management signature verification. This is
+ * the default. When the GRUB is locked down, user cannot change
+ * the value by setting the appendedsig_key_mgmt variable back to
+ * "dynamic".
+ *
+ * "dynamic": Enforce dynamic key management signature verification. When the
+ * GRUB is locked down, user cannot change the value by setting the
+ * appendedsig_key_mgmt variable back to "static".
+ */
+ grub_register_variable_hook ("appendedsig_key_mgmt", grub_env_read_key_mgmt, grub_env_write_key_mgmt);
+ grub_env_export ("appendedsig_key_mgmt");
+
+ rc = grub_asn1_init ();
+ if (rc != ASN1_SUCCESS)
+ grub_fatal ("error initing ASN.1 data structures: %d: %s\n", rc, asn1_strerror (rc));
+
+ /*
+ * If signature verification is enabled with the dynamic key management,
+ * extract trusted and distrusted keys from PKS and store them in the db
+ * and dbx list.
+ */
+ if (append_key_mgmt == true)
+ create_dbs_from_pks ();
+ /*
+ * If signature verification is enabled with the static key management,
+ * extract trusted keys from ELF Note and store them in the db list.
+ */
+ else
+ {
+ load_elf2db ();
+ grub_dprintf ("appendedsig", "the db list now has %u static keys\n",
+ db.cert_entries);
+ }
+
+ cmd_verify = grub_register_command ("append_verify", grub_cmd_verify_signature, N_(""),
+ N_("Verify SIGNED_FILE against the trusted X.509 certificates in the db list"));
+ cmd_list_db = grub_register_command ("append_list_db", grub_cmd_list_db, 0,
+ N_("Show the list of trusted X.509 certificates from the db list"));
+ cmd_db_cert = grub_register_command ("append_add_db_cert", grub_cmd_db_cert, N_(""),
+ N_("Add trusted X509_CERTIFICATE to the db list"));
+ cmd_dbx_cert = grub_register_command ("append_add_dbx_cert", grub_cmd_dbx_cert, N_(""),
+ N_("Add distrusted X509_CERTIFICATE to the dbx list"));
+
+ cmd_list_dbx = grub_register_command ("append_list_dbx", grub_cmd_list_dbx, 0,
+ N_("Show the list of distrusted certificates and"
+ " certificate/binary hashes from the dbx list"));
+ cmd_db_hash = grub_register_command ("append_add_db_hash", grub_cmd_add_db_hash, N_("BINARY HASH FILE"),
+ N_("Add trusted BINARY HASH to the db list."));
+ cmd_dbx_hash = grub_register_extcmd ("append_add_dbx_hash", grub_cmd_add_dbx_hash, 0,
+ N_("[-b|--binary-hash] FILE [BINARY HASH FILE]\n"
+ "[-c|--cert-hash] FILE [CERTFICATE HASH FILE]"),
+ N_("Add distrusted CERTFICATE/BINARY HASH to the dbx list."), options);
+
+ grub_verifier_register (&grub_appendedsig_verifier);
+ grub_dl_set_persistent (mod);
+}
+
+GRUB_MOD_FINI (appendedsig)
+{
+ /*
+ * grub_dl_set_persistent should prevent this from actually running, but it
+ * does still run under emu.
+ */
+
+ free_db_list ();
+ free_dbx_list ();
+ grub_register_variable_hook ("check_appended_signatures", NULL, NULL);
+ grub_env_unset ("check_appended_signatures");
+ grub_register_variable_hook ("appendedsig_key_mgmt", NULL, NULL);
+ grub_env_unset ("appendedsig_key_mgmt");
+ grub_verifier_unregister (&grub_appendedsig_verifier);
+ grub_unregister_command (cmd_verify);
+ grub_unregister_command (cmd_list_db);
+ grub_unregister_command (cmd_db_cert);
+ grub_unregister_command (cmd_dbx_cert);
+ grub_unregister_command (cmd_list_dbx);
+ grub_unregister_command (cmd_db_hash);
+ grub_unregister_extcmd (cmd_dbx_hash);
+}
diff --git a/grub-core/commands/appendedsig/appendedsig.h b/grub-core/commands/appendedsig/appendedsig.h
new file mode 100644
index 000000000..c8746544e
--- /dev/null
+++ b/grub-core/commands/appendedsig/appendedsig.h
@@ -0,0 +1,133 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2020, 2022 Free Software Foundation, Inc.
+ * Copyright (C) 2020, 2022, 2025 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
+
+extern asn1_node grub_gnutls_gnutls_asn;
+extern asn1_node grub_gnutls_pkix_asn;
+
+#define GRUB_MAX_OID_LEN 32
+
+/* RSA public key. */
+#define GRUB_MAX_MPI 2
+#define GRUB_RSA_PK_MODULUS 0
+#define GRUB_RSA_PK_EXPONENT 1
+
+/* Certificate fingerprint. */
+#define GRUB_MAX_FINGERPRINT 3
+#define GRUB_FINGERPRINT_SHA256 0
+#define GRUB_FINGERPRINT_SHA384 1
+#define GRUB_FINGERPRINT_SHA512 2
+
+/* Max size of hash data. */
+#define GRUB_MAX_HASH_LEN 64
+
+/*
+ * One or more x509 certificates. We do limited parsing:
+ * extracting only the version, serial, issuer, subject, RSA public key
+ * and key size.
+ * Also, hold the sha256, sha384, and sha512 fingerprint of the certificate.
+ */
+struct x509_certificate
+{
+ struct x509_certificate *next;
+ grub_uint8_t version;
+ grub_uint8_t *serial;
+ grub_size_t serial_len;
+ char *issuer;
+ grub_size_t issuer_len;
+ char *subject;
+ grub_size_t subject_len;
+ /* We only support RSA public keys. This encodes [modulus, publicExponent]. */
+ gcry_mpi_t mpis[GRUB_MAX_MPI];
+ grub_int32_t modulus_size;
+ grub_uint8_t fingerprint[GRUB_MAX_FINGERPRINT][GRUB_MAX_HASH_LEN];
+};
+typedef struct x509_certificate grub_x509_cert_t;
+
+/* A PKCS#7 signed data signer info. */
+struct pkcs7_signer
+{
+ const gcry_md_spec_t *hash;
+ gcry_mpi_t sig_mpi;
+};
+typedef struct pkcs7_signer grub_pkcs7_signer_t;
+
+/*
+ * A PKCS#7 signed data message. We make no attempt to match intelligently, so
+ * we don't save any info about the signer.
+ */
+struct pkcs7_data
+{
+ grub_int32_t signer_count;
+ grub_pkcs7_signer_t *signers;
+};
+typedef struct pkcs7_data grub_pkcs7_data_t;
+
+/*
+ * Import a DER-encoded certificate at 'data', of size 'size'. Place the results
+ * into 'results', which must be already allocated.
+ */
+extern grub_err_t
+grub_x509_cert_parse (const void *data, grub_size_t size, grub_x509_cert_t *results);
+
+/*
+ * Release all the storage associated with the x509 certificate. If the caller
+ * dynamically allocated the certificate, it must free it. The caller is also
+ * responsible for maintenance of the linked list.
+ */
+extern void
+grub_x509_cert_release (grub_x509_cert_t *cert);
+
+/*
+ * Parse a PKCS#7 message, which must be a signed data message. The message must
+ * be in 'sigbuf' and of size 'data_size'. The result is placed in 'msg', which
+ * must already be allocated.
+ */
+extern grub_err_t
+grub_pkcs7_data_parse (const void *sigbuf, grub_size_t data_size, grub_pkcs7_data_t *msg);
+
+/*
+ * Release all the storage associated with the PKCS#7 message. If the caller
+ * dynamically allocated the message, it must free it.
+ */
+extern void
+grub_pkcs7_data_release (grub_pkcs7_data_t *msg);
+
+/* Do libtasn1 init. */
+extern int
+grub_asn1_init (void);
+
+/*
+ * Read a value from an ASN1 node, allocating memory to store it. It will work
+ * for anything where the size libtasn1 returns is right:
+ * - Integers
+ * - Octet strings
+ * - DER encoding of other structures
+ *
+ * It will _not_ work for things where libtasn1 size requires adjustment:
+ * - Strings that require an extra null byte at the end
+ * - Bit strings because libtasn1 returns the length in bits, not bytes.
+ *
+ * If the function returns a non-NULL value, the caller must free it.
+ */
+extern void *
+grub_asn1_allocate_and_read (asn1_node node, const char *name, const char *friendly_name,
+ grub_int32_t *content_size);
diff --git a/grub-core/commands/appendedsig/asn1util.c b/grub-core/commands/appendedsig/asn1util.c
new file mode 100644
index 000000000..9dd7898ea
--- /dev/null
+++ b/grub-core/commands/appendedsig/asn1util.c
@@ -0,0 +1,99 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2020, 2022 Free Software Foundation, Inc.
+ * Copyright (C) 2020, 2022, 2025 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
+#include
+#include
+#include
+#include
+
+#include "appendedsig.h"
+
+asn1_node grub_gnutls_gnutls_asn = NULL;
+asn1_node grub_gnutls_pkix_asn = NULL;
+
+extern const asn1_static_node grub_gnutls_asn1_tab[];
+extern const asn1_static_node grub_pkix_asn1_tab[];
+
+/*
+ * Read a value from an ASN1 node, allocating memory to store it. It will work
+ * for anything where the size libtasn1 returns is right:
+ * - Integers
+ * - Octet strings
+ * - DER encoding of other structures
+ *
+ * It will _not_ work for things where libtasn1 size requires adjustment:
+ * - Strings that require an extra NULL byte at the end
+ * - Bit strings because libtasn1 returns the length in bits, not bytes.
+ *
+ * If the function returns a non-NULL value, the caller must free it.
+ */
+void *
+grub_asn1_allocate_and_read (asn1_node node, const char *name, const char *friendly_name,
+ grub_int32_t *content_size)
+{
+ grub_int32_t result;
+ grub_uint8_t *tmpstr = NULL;
+ grub_int32_t tmpstr_size = 0;
+
+ result = asn1_read_value (node, name, NULL, &tmpstr_size);
+ if (result != ASN1_MEM_ERROR)
+ {
+ grub_error (GRUB_ERR_BAD_FILE_TYPE, "reading size of %s did not return expected status: %s",
+ friendly_name, asn1_strerror (result)) ;
+ return NULL;
+ }
+
+ tmpstr = grub_malloc (tmpstr_size);
+ if (tmpstr == NULL)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "could not allocate memory to store %s",
+ friendly_name) ;
+ return NULL;
+ }
+
+ result = asn1_read_value (node, name, tmpstr, &tmpstr_size);
+ if (result != ASN1_SUCCESS)
+ {
+ grub_free (tmpstr);
+ grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading %s: %s", friendly_name,
+ asn1_strerror (result)) ;
+ return NULL;
+ }
+
+ *content_size = tmpstr_size;
+
+ return tmpstr;
+}
+
+int
+grub_asn1_init (void)
+{
+ int res;
+
+ res = asn1_array2tree (grub_gnutls_asn1_tab, &grub_gnutls_gnutls_asn, NULL);
+ if (res != ASN1_SUCCESS)
+ return res;
+
+ res = asn1_array2tree (grub_pkix_asn1_tab, &grub_gnutls_pkix_asn, NULL);
+
+ return res;
+}
diff --git a/grub-core/commands/appendedsig/gnutls_asn1_tab.c b/grub-core/commands/appendedsig/gnutls_asn1_tab.c
new file mode 100644
index 000000000..efc0c145a
--- /dev/null
+++ b/grub-core/commands/appendedsig/gnutls_asn1_tab.c
@@ -0,0 +1,148 @@
+#include
+#include
+
+/*
+ * Imported from gnutls.asn.
+ * https://github.com/gnutls/gnutls/blob/master/lib/gnutls.asn
+ */
+const asn1_static_node grub_gnutls_asn1_tab[] = {
+ { "GNUTLS", 536872976, NULL },
+ { NULL, 1073741836, NULL },
+ { "RSAPublicKey", 1610612741, NULL },
+ { "modulus", 1073741827, NULL },
+ { "publicExponent", 3, NULL },
+ { "RSAPrivateKey", 1610612741, NULL },
+ { "version", 1073741827, NULL },
+ { "modulus", 1073741827, NULL },
+ { "publicExponent", 1073741827, NULL },
+ { "privateExponent", 1073741827, NULL },
+ { "prime1", 1073741827, NULL },
+ { "prime2", 1073741827, NULL },
+ { "exponent1", 1073741827, NULL },
+ { "exponent2", 1073741827, NULL },
+ { "coefficient", 1073741827, NULL },
+ { "otherPrimeInfos", 16386, "OtherPrimeInfos"},
+ { "ProvableSeed", 1610612741, NULL },
+ { "algorithm", 1073741836, NULL },
+ { "seed", 7, NULL },
+ { "OtherPrimeInfos", 1612709899, NULL },
+ { "MAX", 1074266122, "1"},
+ { NULL, 2, "OtherPrimeInfo"},
+ { "OtherPrimeInfo", 1610612741, NULL },
+ { "prime", 1073741827, NULL },
+ { "exponent", 1073741827, NULL },
+ { "coefficient", 3, NULL },
+ { "AlgorithmIdentifier", 1610612741, NULL },
+ { "algorithm", 1073741836, NULL },
+ { "parameters", 541081613, NULL },
+ { "algorithm", 1, NULL },
+ { "DigestInfo", 1610612741, NULL },
+ { "digestAlgorithm", 1073741826, "DigestAlgorithmIdentifier"},
+ { "digest", 7, NULL },
+ { "DigestAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"},
+ { "DSAPublicKey", 1073741827, NULL },
+ { "DSAParameters", 1610612741, NULL },
+ { "p", 1073741827, NULL },
+ { "q", 1073741827, NULL },
+ { "g", 3, NULL },
+ { "DSASignatureValue", 1610612741, NULL },
+ { "r", 1073741827, NULL },
+ { "s", 3, NULL },
+ { "DSAPrivateKey", 1610612741, NULL },
+ { "version", 1073741827, NULL },
+ { "p", 1073741827, NULL },
+ { "q", 1073741827, NULL },
+ { "g", 1073741827, NULL },
+ { "Y", 1073741827, NULL },
+ { "priv", 3, NULL },
+ { "DHParameter", 1610612741, NULL },
+ { "prime", 1073741827, NULL },
+ { "base", 1073741827, NULL },
+ { "privateValueLength", 16387, NULL },
+ { "pkcs-11-ec-Parameters", 1610612754, NULL },
+ { "oId", 1073741836, NULL },
+ { "curveName", 31, NULL },
+ { "ECParameters", 1610612754, NULL },
+ { "namedCurve", 12, NULL },
+ { "ECPrivateKey", 1610612741, NULL },
+ { "Version", 1073741827, NULL },
+ { "privateKey", 1073741831, NULL },
+ { "parameters", 1610637314, "ECParameters"},
+ { NULL, 2056, "0"},
+ { "publicKey", 536895494, NULL },
+ { NULL, 2056, "1"},
+ { "PrincipalName", 1610612741, NULL },
+ { "name-type", 1610620931, NULL },
+ { NULL, 2056, "0"},
+ { "name-string", 536879115, NULL },
+ { NULL, 1073743880, "1"},
+ { NULL, 27, NULL },
+ { "KRB5PrincipalName", 1610612741, NULL },
+ { "realm", 1610620955, NULL },
+ { NULL, 2056, "0"},
+ { "principalName", 536879106, "PrincipalName"},
+ { NULL, 2056, "1"},
+ { "RSAPSSParameters", 1610612741, NULL },
+ { "hashAlgorithm", 1610637314, "AlgorithmIdentifier"},
+ { NULL, 2056, "0"},
+ { "maskGenAlgorithm", 1610637314, "AlgorithmIdentifier"},
+ { NULL, 2056, "1"},
+ { "saltLength", 1610653699, NULL },
+ { NULL, 1073741833, "20"},
+ { NULL, 2056, "2"},
+ { "trailerField", 536911875, NULL },
+ { NULL, 1073741833, "1"},
+ { NULL, 2056, "3"},
+ { "RSAOAEPParameters", 1610612741, NULL },
+ { "hashAlgorithm", 1610637314, "AlgorithmIdentifier"},
+ { NULL, 2056, "0"},
+ { "maskGenAlgorithm", 1610637314, "AlgorithmIdentifier"},
+ { NULL, 2056, "1"},
+ { "pSourceFunc", 536895490, "AlgorithmIdentifier"},
+ { NULL, 2056, "2"},
+ { "GOSTParameters", 1610612741, NULL },
+ { "publicKeyParamSet", 1073741836, NULL },
+ { "digestParamSet", 16396, NULL },
+ { "GOSTParametersOld", 1610612741, NULL },
+ { "publicKeyParamSet", 1073741836, NULL },
+ { "digestParamSet", 1073741836, NULL },
+ { "encryptionParamSet", 16396, NULL },
+ { "GOSTPrivateKey", 1073741831, NULL },
+ { "GOSTPrivateKeyOld", 1073741827, NULL },
+ { "IssuerSignTool", 1610612741, NULL },
+ { "signTool", 1073741858, NULL },
+ { "cATool", 1073741858, NULL },
+ { "signToolCert", 1073741858, NULL },
+ { "cAToolCert", 34, NULL },
+ { "Gost28147-89-EncryptedKey", 1610612741, NULL },
+ { "encryptedKey", 1073741831, NULL },
+ { "maskKey", 1610637319, NULL },
+ { NULL, 4104, "0"},
+ { "macKey", 7, NULL },
+ { "SubjectPublicKeyInfo", 1610612741, NULL },
+ { "algorithm", 1073741826, "AlgorithmIdentifier"},
+ { "subjectPublicKey", 6, NULL },
+ { "GostR3410-TransportParameters", 1610612741, NULL },
+ { "encryptionParamSet", 1073741836, NULL },
+ { "ephemeralPublicKey", 1610637314, "SubjectPublicKeyInfo"},
+ { NULL, 4104, "0"},
+ { "ukm", 7, NULL },
+ { "GostR3410-KeyTransport", 1610612741, NULL },
+ { "sessionEncryptedKey", 1073741826, "Gost28147-89-EncryptedKey"},
+ { "transportParameters", 536895490, "GostR3410-TransportParameters"},
+ { NULL, 4104, "0"},
+ { "TPMKey", 1610612741, NULL },
+ { "type", 1073741836, NULL },
+ { "emptyAuth", 1610637316, NULL },
+ { NULL, 2056, "0"},
+ { "parent", 1073741827, NULL },
+ { "pubkey", 1073741831, NULL },
+ { "privkey", 7, NULL },
+ { "MLDSAPrivateKey", 536870917, NULL },
+ { "version", 1073741827, NULL },
+ { "privateKeyAlgorithm", 1073741826, "AlgorithmIdentifier"},
+ { "privateKey", 1073741831, NULL },
+ { "publicKey", 536895495, NULL },
+ { NULL, 2056, "1"},
+ { NULL, 0, NULL }
+};
diff --git a/grub-core/commands/appendedsig/pkcs7.c b/grub-core/commands/appendedsig/pkcs7.c
new file mode 100644
index 000000000..b8e272047
--- /dev/null
+++ b/grub-core/commands/appendedsig/pkcs7.c
@@ -0,0 +1,452 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2020, 2022 Free Software Foundation, Inc.
+ * Copyright (C) 2020, 2022, 2025 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 "appendedsig.h"
+#include
+#include
+#include
+#include
+
+static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
+
+/* RFC 5652 s 5.1. */
+static const char *signedData_oid = "1.2.840.113549.1.7.2";
+
+/* RFC 4055 s 2.1. */
+static const char *sha256_oid = "2.16.840.1.101.3.4.2.1";
+static const char *sha512_oid = "2.16.840.1.101.3.4.2.3";
+
+static grub_err_t
+process_content (grub_uint8_t *content, grub_int32_t size, grub_pkcs7_data_t *msg)
+{
+ grub_int32_t res;
+ asn1_node signed_part;
+ grub_err_t err = GRUB_ERR_NONE;
+ char algo_oid[GRUB_MAX_OID_LEN];
+ grub_int32_t algo_oid_size;
+ grub_int32_t algo_count;
+ grub_int32_t signer_count;
+ grub_int32_t i;
+ char version;
+ grub_int32_t version_size = sizeof (version);
+ grub_uint8_t *result_buf;
+ grub_int32_t result_size = 0;
+ grub_int32_t crls_size = 0;
+ gcry_error_t gcry_err;
+ bool sha256_in_da, sha256_in_si, sha512_in_da, sha512_in_si;
+ char *da_path;
+ char *si_sig_path;
+ char *si_da_path;
+
+ res = asn1_create_element (grub_gnutls_pkix_asn, "PKIX1.pkcs-7-SignedData", &signed_part);
+ if (res != ASN1_SUCCESS)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not create ASN.1 structure for PKCS#7 signed part");
+
+ res = asn1_der_decoding2 (&signed_part, content, &size,
+ ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+ if (res != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "error reading PKCS#7 signed data: %s", asn1_error);
+ goto cleanup_signed_part;
+ }
+
+ /*
+ * SignedData ::= SEQUENCE {
+ * version CMSVersion,
+ * digestAlgorithms DigestAlgorithmIdentifiers,
+ * encapContentInfo EncapsulatedContentInfo,
+ * certificates [0] IMPLICIT CertificateSet OPTIONAL,
+ * crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
+ * signerInfos SignerInfos }
+ */
+
+ res = asn1_read_value (signed_part, "version", &version, &version_size);
+ if (res != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE, "error reading signedData version: %s",
+ asn1_strerror (res));
+ goto cleanup_signed_part;
+ }
+
+ /* Signature version must be 1 because appended signature only support v1. */
+ if (version != 1)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "unexpected signature version v%d, only v1 supported", version);
+ goto cleanup_signed_part;
+ }
+
+ /*
+ * digestAlgorithms DigestAlgorithmIdentifiers
+ *
+ * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
+ * DigestAlgorithmIdentifer is an X.509 AlgorithmIdentifier (10.1.1)
+ *
+ * RFC 4055 s 2.1:
+ * sha256Identifier AlgorithmIdentifier ::= { id-sha256, NULL }
+ * sha512Identifier AlgorithmIdentifier ::= { id-sha512, NULL }
+ *
+ * We only support 1 element in the set, and we do not check parameters atm.
+ */
+ res = asn1_number_of_elements (signed_part, "digestAlgorithms", &algo_count);
+ if (res != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE, "error counting number of digest algorithms: %s",
+ asn1_strerror (res));
+ goto cleanup_signed_part;
+ }
+
+ if (algo_count <= 0)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE, "a minimum of 1 digest algorithm is required");
+ goto cleanup_signed_part;
+ }
+
+ if (algo_count > 2)
+ {
+ err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "a maximum of 2 digest algorithms is supported");
+ goto cleanup_signed_part;
+ }
+
+ sha256_in_da = false;
+ sha512_in_da = false;
+
+ for (i = 0; i < algo_count; i++)
+ {
+ da_path = grub_xasprintf ("digestAlgorithms.?%d.algorithm", i + 1);
+ if (da_path == NULL)
+ {
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not allocate path for digest algorithm parsing path");
+ goto cleanup_signed_part;
+ }
+
+ algo_oid_size = sizeof (algo_oid);
+ res = asn1_read_value (signed_part, da_path, algo_oid, &algo_oid_size);
+ if (res != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE, "error reading digest algorithm: %s",
+ asn1_strerror (res));
+ grub_free (da_path);
+ goto cleanup_signed_part;
+ }
+
+ if (grub_strncmp (sha512_oid, algo_oid, algo_oid_size) == 0)
+ {
+ if (sha512_in_da == false)
+ sha512_in_da = true;
+ else
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "SHA-512 specified twice in digest algorithm list");
+ grub_free (da_path);
+ goto cleanup_signed_part;
+ }
+ }
+ else if (grub_strncmp (sha256_oid, algo_oid, algo_oid_size) == 0)
+ {
+ if (sha256_in_da == false)
+ sha256_in_da = true;
+ else
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "SHA-256 specified twice in digest algorithm list");
+ grub_free (da_path);
+ goto cleanup_signed_part;
+ }
+ }
+ else
+ {
+ err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "only SHA-256 and SHA-512 hashes are supported, found OID %s",
+ algo_oid);
+ grub_free (da_path);
+ goto cleanup_signed_part;
+ }
+
+ grub_free (da_path);
+ }
+
+ /* At this point, at least one of sha{256,512}_in_da must be true. */
+
+ /*
+ * We ignore the certificates, but we don't permit CRLs. A CRL entry might be
+ * revoking the certificate we're using, and we have no way of dealing with
+ * that at the moment.
+ */
+ res = asn1_read_value (signed_part, "crls", NULL, &crls_size);
+ if (res != ASN1_ELEMENT_NOT_FOUND)
+ {
+ err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "PKCS#7 messages with embedded CRLs are not supported");
+ goto cleanup_signed_part;
+ }
+
+ /* Read the signatures */
+ res = asn1_number_of_elements (signed_part, "signerInfos", &signer_count);
+ if (res != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE, "error counting number of signers: %s",
+ asn1_strerror (res));
+ goto cleanup_signed_part;
+ }
+
+ if (signer_count <= 0)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE, "a minimum of 1 signer is required");
+ goto cleanup_signed_part;
+ }
+
+ msg->signers = grub_calloc (signer_count, sizeof (grub_pkcs7_signer_t));
+ if (msg->signers == NULL)
+ {
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not allocate space for %d signers", signer_count);
+ goto cleanup_signed_part;
+ }
+
+ msg->signer_count = 0;
+ for (i = 0; i < signer_count; i++)
+ {
+ si_da_path = grub_xasprintf ("signerInfos.?%d.digestAlgorithm.algorithm", i + 1);
+ if (si_da_path == NULL)
+ {
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not allocate path for signer %d's digest algorithm parsing path",
+ i);
+ goto cleanup_signerInfos;
+ }
+
+ algo_oid_size = sizeof (algo_oid);
+ res = asn1_read_value (signed_part, si_da_path, algo_oid, &algo_oid_size);
+ if (res != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "error reading signer %d's digest algorithm: %s", i, asn1_strerror (res));
+ grub_free (si_da_path);
+ goto cleanup_signerInfos;
+ }
+
+ grub_free (si_da_path);
+
+ if (grub_strncmp (sha512_oid, algo_oid, algo_oid_size) == 0)
+ {
+ if (sha512_in_da == false)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "signer %d claims a SHA-512 signature which was not "
+ "specified in the outer DigestAlgorithms", i);
+ goto cleanup_signerInfos;
+ }
+ else
+ {
+ sha512_in_si = true;
+ msg->signers[i].hash = grub_crypto_lookup_md_by_name ("sha512");
+ }
+ }
+ else if (grub_strncmp (sha256_oid, algo_oid, algo_oid_size) == 0)
+ {
+ if (sha256_in_da == false)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "signer %d claims a SHA-256 signature which was not "
+ "specified in the outer DigestAlgorithms", i);
+ goto cleanup_signerInfos;
+ }
+ else
+ {
+ sha256_in_si = true;
+ msg->signers[i].hash = grub_crypto_lookup_md_by_name ("sha256");
+ }
+ }
+ else
+ {
+ err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "only SHA-256 and SHA-512 hashes are supported, found OID %s",
+ algo_oid);
+ goto cleanup_signerInfos;
+ }
+
+ if (msg->signers[i].hash == NULL)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "Hash algorithm for signer %d (OID %s) not loaded", i, algo_oid);
+ goto cleanup_signerInfos;
+ }
+
+ si_sig_path = grub_xasprintf ("signerInfos.?%d.signature", i + 1);
+ if (si_sig_path == NULL)
+ {
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not allocate path for signer %d's signature parsing path", i);
+ goto cleanup_signerInfos;
+ }
+
+ result_buf = grub_asn1_allocate_and_read (signed_part, si_sig_path, "signature data", &result_size);
+ grub_free (si_sig_path);
+
+ if (result_buf == NULL)
+ {
+ err = grub_errno;
+ goto cleanup_signerInfos;
+ }
+
+ gcry_err = _gcry_mpi_scan (&(msg->signers[i].sig_mpi), GCRYMPI_FMT_USG,
+ result_buf, result_size, NULL);
+ grub_free (result_buf);
+
+ if (gcry_err != GPG_ERR_NO_ERROR)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "error loading signature %d into MPI structure: %d",
+ i, gcry_err);
+ goto cleanup_signerInfos;
+ }
+
+ /*
+ * Use msg->signer_count to track fully populated signerInfos so we know
+ * how many we need to clean up.
+ */
+ msg->signer_count++;
+ }
+
+ /*
+ * Final consistency check of signerInfo.*.digestAlgorithm vs digestAlgorithms
+ * .*.algorithm. An algorithm must be present in both digestAlgorithms and
+ * signerInfo or in neither. We have already checked for an algorithm in
+ * signerInfo that is not in digestAlgorithms, here we check for algorithms in
+ * digestAlgorithms but not in signerInfos.
+ */
+ if (sha512_in_da == true && sha512_in_si == false)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "SHA-512 specified in DigestAlgorithms but did not appear in SignerInfos");
+ goto cleanup_signerInfos;
+ }
+
+ if (sha256_in_da == true && sha256_in_si == false)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "SHA-256 specified in DigestAlgorithms but did not appear in SignerInfos");
+ goto cleanup_signerInfos;
+ }
+
+ asn1_delete_structure (&signed_part);
+
+ return GRUB_ERR_NONE;
+
+ cleanup_signerInfos:
+ for (i = 0; i < msg->signer_count; i++)
+ _gcry_mpi_release (msg->signers[i].sig_mpi);
+
+ grub_free (msg->signers);
+
+ cleanup_signed_part:
+ asn1_delete_structure (&signed_part);
+
+ return err;
+}
+
+grub_err_t
+grub_pkcs7_data_parse (const void *sigbuf, grub_size_t data_size, grub_pkcs7_data_t *msg)
+{
+ grub_int32_t res;
+ asn1_node content_info;
+ grub_err_t err = GRUB_ERR_NONE;
+ char content_oid[GRUB_MAX_OID_LEN];
+ grub_uint8_t *content;
+ grub_int32_t content_size;
+ grub_int32_t content_oid_size = sizeof (content_oid);
+ grub_int32_t size = (grub_int32_t) data_size;
+
+ if (data_size > GRUB_UINT_MAX)
+ return grub_error (GRUB_ERR_OUT_OF_RANGE,
+ "cannot parse a PKCS#7 message where data size > GRUB_UINT_MAX");
+
+ res = asn1_create_element (grub_gnutls_pkix_asn, "PKIX1.pkcs-7-ContentInfo", &content_info);
+ if (res != ASN1_SUCCESS)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not create ASN.1 structure for PKCS#7 data: %s",
+ asn1_strerror (res));
+
+ res = asn1_der_decoding2 (&content_info, sigbuf, &size,
+ ASN1_DECODE_FLAG_STRICT_DER | ASN1_DECODE_FLAG_ALLOW_PADDING,
+ asn1_error);
+ if (res != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "error decoding PKCS#7 message DER: %s", asn1_error);
+ goto cleanup;
+ }
+
+ /*
+ * ContentInfo ::= SEQUENCE {
+ * contentType ContentType,
+ * content [0] EXPLICIT ANY DEFINED BY contentType }
+ *
+ * ContentType ::= OBJECT IDENTIFIER
+ */
+ res = asn1_read_value (content_info, "contentType", content_oid, &content_oid_size);
+ if (res != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE, "error reading PKCS#7 content type: %s",
+ asn1_strerror (res));
+ goto cleanup;
+ }
+
+ /* OID for SignedData defined in 5.1. */
+ if (grub_strncmp (signedData_oid, content_oid, content_oid_size) != 0)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "unexpected content type in PKCS#7 message: OID %s", content_oid);
+ goto cleanup;
+ }
+
+ content = grub_asn1_allocate_and_read (content_info, "content", "PKCS#7 message content", &content_size);
+ if (content == NULL)
+ {
+ err = grub_errno;
+ goto cleanup;
+ }
+
+ err = process_content (content, content_size, msg);
+ grub_free (content);
+
+ cleanup:
+ asn1_delete_structure (&content_info);
+
+ return err;
+}
+
+/*
+ * Release all the storage associated with the PKCS#7 message. If the caller
+ * dynamically allocated the message, it must free it.
+ */
+void
+grub_pkcs7_data_release (grub_pkcs7_data_t *msg)
+{
+ grub_int32_t i;
+
+ for (i = 0; i < msg->signer_count; i++)
+ _gcry_mpi_release (msg->signers[i].sig_mpi);
+
+ grub_free (msg->signers);
+}
diff --git a/grub-core/commands/appendedsig/pkix_asn1_tab.c b/grub-core/commands/appendedsig/pkix_asn1_tab.c
new file mode 100644
index 000000000..ec5f87bfd
--- /dev/null
+++ b/grub-core/commands/appendedsig/pkix_asn1_tab.c
@@ -0,0 +1,485 @@
+#include
+#include
+
+/*
+ * Imported from pkix.asn.
+ * https://github.com/gnutls/gnutls/blob/master/lib/pkix.asn
+ */
+const asn1_static_node grub_pkix_asn1_tab[] = {
+ { "PKIX1", 536875024, NULL },
+ { NULL, 1073741836, NULL },
+ { "PrivateKeyUsagePeriod", 1610612741, NULL },
+ { "notBefore", 1610637349, NULL },
+ { NULL, 4104, "0"},
+ { "notAfter", 536895525, NULL },
+ { NULL, 4104, "1"},
+ { "AuthorityKeyIdentifier", 1610612741, NULL },
+ { "keyIdentifier", 1610637319, NULL },
+ { NULL, 4104, "0"},
+ { "authorityCertIssuer", 1610637314, "GeneralNames"},
+ { NULL, 4104, "1"},
+ { "authorityCertSerialNumber", 536895490, "CertificateSerialNumber"},
+ { NULL, 4104, "2"},
+ { "SubjectKeyIdentifier", 1073741831, NULL },
+ { "KeyUsage", 1073741830, NULL },
+ { "DirectoryString", 1610612754, NULL },
+ { "teletexString", 1612709918, NULL },
+ { "MAX", 524298, "1"},
+ { "printableString", 1612709919, NULL },
+ { "MAX", 524298, "1"},
+ { "universalString", 1612709920, NULL },
+ { "MAX", 524298, "1"},
+ { "utf8String", 1612709922, NULL },
+ { "MAX", 524298, "1"},
+ { "bmpString", 538968097, NULL },
+ { "MAX", 524298, "1"},
+ { "SubjectAltName", 1073741826, "GeneralNames"},
+ { "GeneralNames", 1612709899, NULL },
+ { "MAX", 1074266122, "1"},
+ { NULL, 2, "GeneralName"},
+ { "GeneralName", 1610612754, NULL },
+ { "otherName", 1610620930, "AnotherName"},
+ { NULL, 4104, "0"},
+ { "rfc822Name", 1610620957, NULL },
+ { NULL, 4104, "1"},
+ { "dNSName", 1610620957, NULL },
+ { NULL, 4104, "2"},
+ { "x400Address", 1610620941, NULL },
+ { NULL, 4104, "3"},
+ { "directoryName", 1610620939, NULL },
+ { NULL, 1073743880, "4"},
+ { NULL, 2, "RelativeDistinguishedName"},
+ { "ediPartyName", 1610620941, NULL },
+ { NULL, 4104, "5"},
+ { "uniformResourceIdentifier", 1610620957, NULL },
+ { NULL, 4104, "6"},
+ { "iPAddress", 1610620935, NULL },
+ { NULL, 4104, "7"},
+ { "registeredID", 536879116, NULL },
+ { NULL, 4104, "8"},
+ { "AnotherName", 1610612741, NULL },
+ { "type-id", 1073741836, NULL },
+ { "value", 541073421, NULL },
+ { NULL, 1073743880, "0"},
+ { "type-id", 1, NULL },
+ { "IssuerAltName", 1073741826, "GeneralNames"},
+ { "BasicConstraints", 1610612741, NULL },
+ { "cA", 1610645508, NULL },
+ { NULL, 131081, NULL },
+ { "pathLenConstraint", 16387, NULL },
+ { "CRLDistributionPoints", 1612709899, NULL },
+ { "MAX", 1074266122, "1"},
+ { NULL, 2, "DistributionPoint"},
+ { "DistributionPoint", 1610612741, NULL },
+ { "distributionPoint", 1610637314, "DistributionPointName"},
+ { NULL, 2056, "0"},
+ { "reasons", 1610637314, "ReasonFlags"},
+ { NULL, 4104, "1"},
+ { "cRLIssuer", 536895490, "GeneralNames"},
+ { NULL, 4104, "2"},
+ { "DistributionPointName", 1610612754, NULL },
+ { "fullName", 1610620930, "GeneralNames"},
+ { NULL, 4104, "0"},
+ { "nameRelativeToCRLIssuer", 536879106, "RelativeDistinguishedName"},
+ { NULL, 4104, "1"},
+ { "ReasonFlags", 1073741830, NULL },
+ { "ExtKeyUsageSyntax", 1612709899, NULL },
+ { "MAX", 1074266122, "1"},
+ { NULL, 12, NULL },
+ { "AuthorityInfoAccessSyntax", 1612709899, NULL },
+ { "MAX", 1074266122, "1"},
+ { NULL, 2, "AccessDescription"},
+ { "AccessDescription", 1610612741, NULL },
+ { "accessMethod", 1073741836, NULL },
+ { "accessLocation", 2, "GeneralName"},
+ { "Attribute", 1610612741, NULL },
+ { "type", 1073741836, NULL },
+ { "values", 536870927, NULL },
+ { NULL, 13, NULL },
+ { "AttributeTypeAndValue", 1610612741, NULL },
+ { "type", 1073741836, NULL },
+ { "value", 13, NULL },
+ { "Name", 1610612754, NULL },
+ { "rdnSequence", 536870923, NULL },
+ { NULL, 2, "RelativeDistinguishedName"},
+ { "DistinguishedName", 1610612747, NULL },
+ { NULL, 2, "RelativeDistinguishedName"},
+ { "RelativeDistinguishedName", 1612709903, NULL },
+ { "MAX", 1074266122, "1"},
+ { NULL, 2, "AttributeTypeAndValue"},
+ { "Certificate", 1610612741, NULL },
+ { "tbsCertificate", 1073741826, "TBSCertificate"},
+ { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
+ { "signature", 6, NULL },
+ { "TBSCertificate", 1610612741, NULL },
+ { "version", 1610653699, NULL },
+ { NULL, 1073741833, "0"},
+ { NULL, 2056, "0"},
+ { "serialNumber", 1073741826, "CertificateSerialNumber"},
+ { "signature", 1073741826, "AlgorithmIdentifier"},
+ { "issuer", 1073741826, "Name"},
+ { "validity", 1073741826, "Validity"},
+ { "subject", 1073741826, "Name"},
+ { "subjectPublicKeyInfo", 1073741826, "SubjectPublicKeyInfo"},
+ { "issuerUniqueID", 1610637314, "UniqueIdentifier"},
+ { NULL, 4104, "1"},
+ { "subjectUniqueID", 1610637314, "UniqueIdentifier"},
+ { NULL, 4104, "2"},
+ { "extensions", 536895490, "Extensions"},
+ { NULL, 2056, "3"},
+ { "CertificateSerialNumber", 1073741827, NULL },
+ { "Validity", 1610612741, NULL },
+ { "notBefore", 1073741826, "Time"},
+ { "notAfter", 2, "Time"},
+ { "Time", 1610612754, NULL },
+ { "utcTime", 1073741860, NULL },
+ { "generalTime", 37, NULL },
+ { "UniqueIdentifier", 1073741830, NULL },
+ { "SubjectPublicKeyInfo", 1610612741, NULL },
+ { "algorithm", 1073741826, "AlgorithmIdentifier"},
+ { "subjectPublicKey", 6, NULL },
+ { "Extensions", 1612709899, NULL },
+ { "MAX", 1074266122, "1"},
+ { NULL, 2, "Extension"},
+ { "Extension", 1610612741, NULL },
+ { "extnID", 1073741836, NULL },
+ { "critical", 1610645508, NULL },
+ { NULL, 131081, NULL },
+ { "extnValue", 7, NULL },
+ { "CertificateList", 1610612741, NULL },
+ { "tbsCertList", 1073741826, "TBSCertList"},
+ { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
+ { "signature", 6, NULL },
+ { "TBSCertList", 1610612741, NULL },
+ { "version", 1073758211, NULL },
+ { "signature", 1073741826, "AlgorithmIdentifier"},
+ { "issuer", 1073741826, "Name"},
+ { "thisUpdate", 1073741826, "Time"},
+ { "nextUpdate", 1073758210, "Time"},
+ { "revokedCertificates", 1610629131, NULL },
+ { NULL, 536870917, NULL },
+ { "userCertificate", 1073741826, "CertificateSerialNumber"},
+ { "revocationDate", 1073741826, "Time"},
+ { "crlEntryExtensions", 16386, "Extensions"},
+ { "crlExtensions", 536895490, "Extensions"},
+ { NULL, 2056, "0"},
+ { "AlgorithmIdentifier", 1610612741, NULL },
+ { "algorithm", 1073741836, NULL },
+ { "parameters", 541081613, NULL },
+ { "algorithm", 1, NULL },
+ { "Dss-Sig-Value", 1610612741, NULL },
+ { "r", 1073741827, NULL },
+ { "s", 3, NULL },
+ { "Dss-Parms", 1610612741, NULL },
+ { "p", 1073741827, NULL },
+ { "q", 1073741827, NULL },
+ { "g", 3, NULL },
+ { "pkcs-7-ContentInfo", 1610612741, NULL },
+ { "contentType", 1073741836, NULL },
+ { "content", 541073421, NULL },
+ { NULL, 1073743880, "0"},
+ { "contentType", 1, NULL },
+ { "pkcs-7-DigestInfo", 1610612741, NULL },
+ { "digestAlgorithm", 1073741826, "AlgorithmIdentifier"},
+ { "digest", 7, NULL },
+ { "pkcs-7-SignedData", 1610612741, NULL },
+ { "version", 1073741827, NULL },
+ { "digestAlgorithms", 1073741826, "pkcs-7-DigestAlgorithmIdentifiers"},
+ { "encapContentInfo", 1073741826, "pkcs-7-EncapsulatedContentInfo"},
+ { "certificates", 1610637314, "pkcs-7-CertificateSet"},
+ { NULL, 4104, "0"},
+ { "crls", 1610637314, "pkcs-7-CertificateRevocationLists"},
+ { NULL, 4104, "1"},
+ { "signerInfos", 2, "pkcs-7-SignerInfos"},
+ { "pkcs-7-DigestAlgorithmIdentifiers", 1610612751, NULL },
+ { NULL, 2, "AlgorithmIdentifier"},
+ { "pkcs-7-EncapsulatedContentInfo", 1610612741, NULL },
+ { "eContentType", 1073741836, NULL },
+ { "eContent", 536895501, NULL },
+ { NULL, 2056, "0"},
+ { "pkcs-7-CertificateRevocationLists", 1610612751, NULL },
+ { NULL, 13, NULL },
+ { "pkcs-7-CertificateChoices", 1610612754, NULL },
+ { "certificate", 13, NULL },
+ { "pkcs-7-CertificateSet", 1610612751, NULL },
+ { NULL, 2, "pkcs-7-CertificateChoices"},
+ { "IssuerAndSerialNumber", 1610612741, NULL },
+ { "issuer", 1073741826, "Name"},
+ { "serialNumber", 2, "CertificateSerialNumber"},
+ { "pkcs-7-SignerInfo", 1610612741, NULL },
+ { "version", 1073741827, NULL },
+ { "sid", 1073741826, "SignerIdentifier"},
+ { "digestAlgorithm", 1073741826, "AlgorithmIdentifier"},
+ { "signedAttrs", 1610637314, "SignedAttributes"},
+ { NULL, 4104, "0"},
+ { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
+ { "signature", 1073741831, NULL },
+ { "unsignedAttrs", 536895490, "SignedAttributes"},
+ { NULL, 4104, "1"},
+ { "SignedAttributes", 1612709903, NULL },
+ { "MAX", 1074266122, "1"},
+ { NULL, 2, "Attribute"},
+ { "SignerIdentifier", 1610612754, NULL },
+ { "issuerAndSerialNumber", 1073741826, "IssuerAndSerialNumber"},
+ { "subjectKeyIdentifier", 536879111, NULL },
+ { NULL, 4104, "0"},
+ { "pkcs-7-SignerInfos", 1610612751, NULL },
+ { NULL, 2, "pkcs-7-SignerInfo"},
+ { "pkcs-10-CertificationRequestInfo", 1610612741, NULL },
+ { "version", 1073741827, NULL },
+ { "subject", 1073741826, "Name"},
+ { "subjectPKInfo", 1073741826, "SubjectPublicKeyInfo"},
+ { "attributes", 536879106, "Attributes"},
+ { NULL, 4104, "0"},
+ { "Attributes", 1610612751, NULL },
+ { NULL, 2, "Attribute"},
+ { "pkcs-10-CertificationRequest", 1610612741, NULL },
+ { "certificationRequestInfo", 1073741826, "pkcs-10-CertificationRequestInfo"},
+ { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
+ { "signature", 6, NULL },
+ { "pkcs-9-at-challengePassword", 1879048204, NULL },
+ { "iso", 1073741825, "1"},
+ { "member-body", 1073741825, "2"},
+ { "us", 1073741825, "840"},
+ { "rsadsi", 1073741825, "113549"},
+ { "pkcs", 1073741825, "1"},
+ { NULL, 1073741825, "9"},
+ { NULL, 1, "7"},
+ { "pkcs-9-challengePassword", 1610612754, NULL },
+ { "printableString", 1073741855, NULL },
+ { "utf8String", 34, NULL },
+ { "pkcs-9-localKeyId", 1073741831, NULL },
+ { "pkcs-8-PrivateKeyInfo", 1610612741, NULL },
+ { "version", 1073741827, NULL },
+ { "privateKeyAlgorithm", 1073741826, "AlgorithmIdentifier"},
+ { "privateKey", 1073741831, NULL },
+ { "attributes", 536895490, "Attributes"},
+ { NULL, 4104, "0"},
+ { "pkcs-8-EncryptedPrivateKeyInfo", 1610612741, NULL },
+ { "encryptionAlgorithm", 1073741826, "AlgorithmIdentifier"},
+ { "encryptedData", 2, "pkcs-8-EncryptedData"},
+ { "pkcs-8-EncryptedData", 1073741831, NULL },
+ { "pkcs-5-des-CBC-params", 1612709895, NULL },
+ { NULL, 1048586, "8"},
+ { "pkcs-5-des-EDE3-CBC-params", 1612709895, NULL },
+ { NULL, 1048586, "8"},
+ { "pkcs-5-aes128-CBC-params", 1612709895, NULL },
+ { NULL, 1048586, "16"},
+ { "pkcs-5-aes192-CBC-params", 1612709895, NULL },
+ { NULL, 1048586, "16"},
+ { "pkcs-5-aes256-CBC-params", 1612709895, NULL },
+ { NULL, 1048586, "16"},
+ { "Gost28147-89-Parameters", 1610612741, NULL },
+ { "iv", 1073741831, NULL },
+ { "encryptionParamSet", 12, NULL },
+ { "pkcs-5-PBE-params", 1610612741, NULL },
+ { "salt", 1073741831, NULL },
+ { "iterationCount", 3, NULL },
+ { "pkcs-5-PBES2-params", 1610612741, NULL },
+ { "keyDerivationFunc", 1073741826, "AlgorithmIdentifier"},
+ { "encryptionScheme", 2, "AlgorithmIdentifier"},
+ { "pkcs-5-PBMAC1-params", 1610612741, NULL },
+ { "keyDerivationFunc", 1073741826, "AlgorithmIdentifier"},
+ { "messageAuthScheme", 2, "AlgorithmIdentifier"},
+ { "pkcs-5-PBKDF2-params", 1610612741, NULL },
+ { "salt", 1610612754, NULL },
+ { "specified", 1073741831, NULL },
+ { "otherSource", 2, "AlgorithmIdentifier"},
+ { "iterationCount", 1073741827, NULL },
+ { "keyLength", 1073758211, NULL },
+ { "prf", 16386, "AlgorithmIdentifier"},
+ { "pkcs-12-PFX", 1610612741, NULL },
+ { "version", 1610874883, NULL },
+ { "v3", 1, "3"},
+ { "authSafe", 1073741826, "pkcs-7-ContentInfo"},
+ { "macData", 16386, "pkcs-12-MacData"},
+ { "pkcs-12-PbeParams", 1610612741, NULL },
+ { "salt", 1073741831, NULL },
+ { "iterations", 3, NULL },
+ { "pkcs-12-MacData", 1610612741, NULL },
+ { "mac", 1073741826, "pkcs-7-DigestInfo"},
+ { "macSalt", 1073741831, NULL },
+ { "iterations", 536903683, NULL },
+ { NULL, 9, "1"},
+ { "pkcs-12-AuthenticatedSafe", 1610612747, NULL },
+ { NULL, 2, "pkcs-7-ContentInfo"},
+ { "pkcs-12-SafeContents", 1610612747, NULL },
+ { NULL, 2, "pkcs-12-SafeBag"},
+ { "pkcs-12-SafeBag", 1610612741, NULL },
+ { "bagId", 1073741836, NULL },
+ { "bagValue", 1614815245, NULL },
+ { NULL, 1073743880, "0"},
+ { "badId", 1, NULL },
+ { "bagAttributes", 536887311, NULL },
+ { NULL, 2, "Attribute"},
+ { "pkcs-12-CertBag", 1610612741, NULL },
+ { "certId", 1073741836, NULL },
+ { "certValue", 541073421, NULL },
+ { NULL, 1073743880, "0"},
+ { "certId", 1, NULL },
+ { "pkcs-12-CRLBag", 1610612741, NULL },
+ { "crlId", 1073741836, NULL },
+ { "crlValue", 541073421, NULL },
+ { NULL, 1073743880, "0"},
+ { "crlId", 1, NULL },
+ { "pkcs-12-SecretBag", 1610612741, NULL },
+ { "secretTypeId", 1073741836, NULL },
+ { "secretValue", 541073421, NULL },
+ { NULL, 1073743880, "0"},
+ { "secretTypeId", 1, NULL },
+ { "pkcs-7-Data", 1073741831, NULL },
+ { "pkcs-7-EncryptedData", 1610612741, NULL },
+ { "version", 1073741827, NULL },
+ { "encryptedContentInfo", 1073741826, "pkcs-7-EncryptedContentInfo"},
+ { "unprotectedAttrs", 536895490, "pkcs-7-UnprotectedAttributes"},
+ { NULL, 4104, "1"},
+ { "pkcs-7-EncryptedContentInfo", 1610612741, NULL },
+ { "contentType", 1073741836, NULL },
+ { "contentEncryptionAlgorithm", 1073741826, "pkcs-7-ContentEncryptionAlgorithmIdentifier"},
+ { "encryptedContent", 536895495, NULL },
+ { NULL, 4104, "0"},
+ { "pkcs-7-ContentEncryptionAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"},
+ { "pkcs-7-UnprotectedAttributes", 1612709903, NULL },
+ { "MAX", 1074266122, "1"},
+ { NULL, 2, "Attribute"},
+ { "ProxyCertInfo", 1610612741, NULL },
+ { "pCPathLenConstraint", 1073758211, NULL },
+ { "proxyPolicy", 2, "ProxyPolicy"},
+ { "ProxyPolicy", 1610612741, NULL },
+ { "policyLanguage", 1073741836, NULL },
+ { "policy", 16391, NULL },
+ { "certificatePolicies", 1612709899, NULL },
+ { "MAX", 1074266122, "1"},
+ { NULL, 2, "PolicyInformation"},
+ { "PolicyInformation", 1610612741, NULL },
+ { "policyIdentifier", 1073741836, NULL },
+ { "policyQualifiers", 538984459, NULL },
+ { "MAX", 1074266122, "1"},
+ { NULL, 2, "PolicyQualifierInfo"},
+ { "PolicyQualifierInfo", 1610612741, NULL },
+ { "policyQualifierId", 1073741836, NULL },
+ { "qualifier", 541065229, NULL },
+ { "policyQualifierId", 1, NULL },
+ { "CPSuri", 1073741853, NULL },
+ { "UserNotice", 1610612741, NULL },
+ { "noticeRef", 1073758210, "NoticeReference"},
+ { "explicitText", 16386, "DisplayText"},
+ { "NoticeReference", 1610612741, NULL },
+ { "organization", 1073741826, "DisplayText"},
+ { "noticeNumbers", 536870923, NULL },
+ { NULL, 3, NULL },
+ { "DisplayText", 1610612754, NULL },
+ { "ia5String", 1612709917, NULL },
+ { "200", 524298, "1"},
+ { "visibleString", 1612709923, NULL },
+ { "200", 524298, "1"},
+ { "bmpString", 1612709921, NULL },
+ { "200", 524298, "1"},
+ { "utf8String", 538968098, NULL },
+ { "200", 524298, "1"},
+ { "OCSPRequest", 1610612741, NULL },
+ { "tbsRequest", 1073741826, "TBSRequest"},
+ { "optionalSignature", 536895490, "Signature"},
+ { NULL, 2056, "0"},
+ { "TBSRequest", 1610612741, NULL },
+ { "version", 1610653699, NULL },
+ { NULL, 1073741833, "0"},
+ { NULL, 2056, "0"},
+ { "requestorName", 1610637314, "GeneralName"},
+ { NULL, 2056, "1"},
+ { "requestList", 1610612747, NULL },
+ { NULL, 2, "Request"},
+ { "requestExtensions", 536895490, "Extensions"},
+ { NULL, 2056, "2"},
+ { "Signature", 1610612741, NULL },
+ { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
+ { "signature", 1073741830, NULL },
+ { "certs", 536895499, NULL },
+ { NULL, 1073743880, "0"},
+ { NULL, 2, "Certificate"},
+ { "Request", 1610612741, NULL },
+ { "reqCert", 1073741826, "CertID"},
+ { "singleRequestExtensions", 536895490, "Extensions"},
+ { NULL, 2056, "0"},
+ { "CertID", 1610612741, NULL },
+ { "hashAlgorithm", 1073741826, "AlgorithmIdentifier"},
+ { "issuerNameHash", 1073741831, NULL },
+ { "issuerKeyHash", 1073741831, NULL },
+ { "serialNumber", 2, "CertificateSerialNumber"},
+ { "OCSPResponse", 1610612741, NULL },
+ { "responseStatus", 1073741826, "OCSPResponseStatus"},
+ { "responseBytes", 536895490, "ResponseBytes"},
+ { NULL, 2056, "0"},
+ { "OCSPResponseStatus", 1610874901, NULL },
+ { "successful", 1073741825, "0"},
+ { "malformedRequest", 1073741825, "1"},
+ { "internalError", 1073741825, "2"},
+ { "tryLater", 1073741825, "3"},
+ { "sigRequired", 1073741825, "5"},
+ { "unauthorized", 1, "6"},
+ { "ResponseBytes", 1610612741, NULL },
+ { "responseType", 1073741836, NULL },
+ { "response", 7, NULL },
+ { "BasicOCSPResponse", 1610612741, NULL },
+ { "tbsResponseData", 1073741826, "ResponseData"},
+ { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
+ { "signature", 1073741830, NULL },
+ { "certs", 536895499, NULL },
+ { NULL, 1073743880, "0"},
+ { NULL, 2, "Certificate"},
+ { "ResponseData", 1610612741, NULL },
+ { "version", 1610653699, NULL },
+ { NULL, 1073741833, "0"},
+ { NULL, 2056, "0"},
+ { "responderID", 1073741826, "ResponderID"},
+ { "producedAt", 1073741861, NULL },
+ { "responses", 1610612747, NULL },
+ { NULL, 2, "SingleResponse"},
+ { "responseExtensions", 536895490, "Extensions"},
+ { NULL, 2056, "1"},
+ { "ResponderID", 1610612754, NULL },
+ { "byName", 1610620939, NULL },
+ { NULL, 1073743880, "1"},
+ { NULL, 2, "RelativeDistinguishedName"},
+ { "byKey", 536879111, NULL },
+ { NULL, 2056, "2"},
+ { "SingleResponse", 1610612741, NULL },
+ { "certID", 1073741826, "CertID"},
+ { "certStatus", 1073741826, "CertStatus"},
+ { "thisUpdate", 1073741861, NULL },
+ { "nextUpdate", 1610637349, NULL },
+ { NULL, 2056, "0"},
+ { "singleExtensions", 536895490, "Extensions"},
+ { NULL, 2056, "1"},
+ { "CertStatus", 1610612754, NULL },
+ { "good", 1610620948, NULL },
+ { NULL, 4104, "0"},
+ { "revoked", 1610620930, "RevokedInfo"},
+ { NULL, 4104, "1"},
+ { "unknown", 536879106, "UnknownInfo"},
+ { NULL, 4104, "2"},
+ { "RevokedInfo", 1610612741, NULL },
+ { "revocationTime", 1073741861, NULL },
+ { "revocationReason", 537157653, NULL },
+ { NULL, 1073743880, "0"},
+ { "unspecified", 1, "0"},
+ { "UnknownInfo", 1073741844, NULL },
+ { "NameConstraints", 1610612741, NULL },
+ { "permittedSubtrees", 1610637314, "GeneralSubtrees"},
+ { NULL, 4104, "0"},
+ { "excludedSubtrees", 536895490, "GeneralSubtrees"},
+ { NULL, 4104, "1"},
+ { "GeneralSubtrees", 1612709899, NULL },
+ { "MAX", 1074266122, "1"},
+ { NULL, 2, "GeneralSubtree"},
+ { "GeneralSubtree", 1610612741, NULL },
+ { "base", 1073741826, "GeneralName"},
+ { "minimum", 1610653699, NULL },
+ { NULL, 1073741833, "0"},
+ { NULL, 4104, "0"},
+ { "maximum", 536895491, NULL },
+ { NULL, 4104, "1"},
+ { "TlsFeatures", 536870923, NULL },
+ { NULL, 3, NULL },
+ { NULL, 0, NULL }
+};
diff --git a/grub-core/commands/appendedsig/x509.c b/grub-core/commands/appendedsig/x509.c
new file mode 100644
index 000000000..bc152663a
--- /dev/null
+++ b/grub-core/commands/appendedsig/x509.c
@@ -0,0 +1,970 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2020, 2022 Free Software Foundation, Inc.
+ * Copyright (C) 2020, 2022, 2025 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
+#include
+#include
+#include
+#include
+
+#include "appendedsig.h"
+
+static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
+
+
+/* RFC 3279 2.3.1 RSA Keys. */
+static const char *rsaEncryption_oid = "1.2.840.113549.1.1.1";
+
+/* RFC 5280 Appendix A. */
+static const char *commonName_oid = "2.5.4.3";
+
+/* RFC 5280 4.2.1.3 Key Usage. */
+static const char *keyUsage_oid = "2.5.29.15";
+
+static const grub_uint8_t digitalSignatureUsage = 0x80;
+
+/* RFC 5280 4.2.1.9 Basic Constraints. */
+static const char *basicConstraints_oid = "2.5.29.19";
+
+/* RFC 5280 4.2.1.12 Extended Key Usage. */
+static const char *extendedKeyUsage_oid = "2.5.29.37";
+static const char *codeSigningUsage_oid = "1.3.6.1.5.5.7.3.3";
+
+/*
+ * RFC 3279 2.3.1
+ *
+ * The RSA public key MUST be encoded using the ASN.1 type RSAPublicKey:
+ *
+ * RSAPublicKey ::= SEQUENCE {
+ * modulus INTEGER, -- n
+ * publicExponent INTEGER } -- e
+ *
+ * where modulus is the modulus n, and publicExponent is the public exponent e.
+ */
+static grub_err_t
+grub_parse_rsa_pubkey (grub_uint8_t *der, grub_int32_t dersize, grub_x509_cert_t *certificate)
+{
+ grub_int32_t result;
+ asn1_node spk = NULL;
+ grub_uint8_t *m_data, *e_data;
+ grub_int32_t m_size, e_size;
+ grub_err_t err = GRUB_ERR_NONE;
+ gcry_error_t gcry_err;
+
+ result = asn1_create_element (grub_gnutls_gnutls_asn, "GNUTLS.RSAPublicKey", &spk);
+ if (result != ASN1_SUCCESS)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "cannot create storage for public key ASN.1 data");
+
+ result = asn1_der_decoding2 (&spk, der, &dersize, ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+ if (result != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "cannot decode certificate public key DER: %s", asn1_error);
+ goto cleanup;
+ }
+
+ m_data = grub_asn1_allocate_and_read (spk, "modulus", "RSA modulus", &m_size);
+ if (m_data == NULL)
+ {
+ err = grub_errno;
+ goto cleanup;
+ }
+
+ e_data = grub_asn1_allocate_and_read (spk, "publicExponent", "RSA public exponent", &e_size);
+ if (e_data == NULL)
+ {
+ err = grub_errno;
+ goto cleanup_m_data;
+ }
+
+ /*
+ * Convert m, e to mpi
+ *
+ * nscanned is not set for FMT_USG, it's only set for FMT_PGP, so we can't
+ * verify it.
+ */
+ gcry_err = _gcry_mpi_scan (&certificate->mpis[0], GCRYMPI_FMT_USG, m_data, m_size, NULL);
+ if (gcry_err != GPG_ERR_NO_ERROR)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "error loading RSA modulus into MPI structure: %d", gcry_err);
+ goto cleanup_e_data;
+ }
+
+ gcry_err = _gcry_mpi_scan (&certificate->mpis[1], GCRYMPI_FMT_USG, e_data, e_size, NULL);
+ if (gcry_err != GPG_ERR_NO_ERROR)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "error loading RSA exponent into MPI structure: %d", gcry_err);
+ goto cleanup_m_mpi;
+ }
+
+ /* RSA key size in bits. */
+ certificate->modulus_size = (m_size * 8) - 8;
+
+ grub_free (e_data);
+ grub_free (m_data);
+ asn1_delete_structure (&spk);
+
+ return GRUB_ERR_NONE;
+
+ cleanup_m_mpi:
+ _gcry_mpi_release (certificate->mpis[0]);
+ cleanup_e_data:
+ grub_free (e_data);
+ cleanup_m_data:
+ grub_free (m_data);
+ cleanup:
+ asn1_delete_structure (&spk);
+
+ return err;
+}
+
+/*
+ * RFC 5280:
+ * SubjectPublicKeyInfo ::= SEQUENCE {
+ * algorithm AlgorithmIdentifier,
+ * subjectPublicKey BIT STRING }
+ *
+ * AlgorithmIdentifiers come from RFC 3279, we are not strictly compilant as we
+ * only support RSA Encryption.
+ */
+static grub_err_t
+grub_x509_read_subject_public_key (asn1_node asn, grub_x509_cert_t *results)
+{
+ grub_int32_t result;
+ grub_err_t err;
+ const char *algo_name = "tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm";
+ const char *params_name = "tbsCertificate.subjectPublicKeyInfo.algorithm.parameters";
+ const char *pk_name = "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey";
+ char algo_oid[GRUB_MAX_OID_LEN];
+ grub_int32_t algo_size = sizeof (algo_oid);
+ char params_value[2];
+ grub_int32_t params_size = sizeof (params_value);
+ grub_uint8_t *key_data = NULL;
+ grub_int32_t key_size = 0;
+ grub_uint32_t key_type;
+
+ /* Algorithm: see notes for rsaEncryption_oid. */
+ result = asn1_read_value (asn, algo_name, algo_oid, &algo_size);
+ if (result != ASN1_SUCCESS)
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading x509 public key algorithm: %s",
+ asn1_strerror (result));
+
+ if (grub_strncmp (algo_oid, rsaEncryption_oid, sizeof (rsaEncryption_oid)) != 0)
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "unsupported x509 public key algorithm: %s", algo_oid);
+
+ /*
+ * RFC 3279 2.3.1 : The rsaEncryption OID is intended to be used in the
+ * algorithm field of a value of type AlgorithmIdentifier. The parameters
+ * field MUST have ASN.1 type NULL for this algorithm identifier.
+ */
+ result = asn1_read_value (asn, params_name, params_value, ¶ms_size);
+ if (result != ASN1_SUCCESS)
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading x509 public key parameters: %s",
+ asn1_strerror (result));
+
+ if (params_value[0] != ASN1_TAG_NULL)
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "invalid x509 public key parameters: expected NULL");
+
+ /*
+ * RFC 3279 2.3.1: The DER encoded RSAPublicKey is the value of the BIT
+ * STRING subjectPublicKey.
+ */
+ result = asn1_read_value_type (asn, pk_name, NULL, &key_size, &key_type);
+ if (result != ASN1_MEM_ERROR)
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading size of x509 public key: %s",
+ asn1_strerror (result));
+ if (key_type != ASN1_ETYPE_BIT_STRING)
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "unexpected ASN.1 type when reading x509 public key: %x",
+ key_type);
+
+ /* Length is in bits. */
+ key_size = (key_size + 7) / 8;
+
+ key_data = grub_malloc (key_size);
+ if (key_data == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory for x509 public key");
+
+ result = asn1_read_value (asn, pk_name, key_data, &key_size);
+ if (result != ASN1_SUCCESS)
+ {
+ grub_free (key_data);
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading public key data");
+ }
+
+ key_size = (key_size + 7) / 8;
+ err = grub_parse_rsa_pubkey (key_data, key_size, results);
+ grub_free (key_data);
+
+ return err;
+}
+
+/* Decode a string as defined in Appendix A. */
+static grub_err_t
+decode_string (char *der, grub_int32_t der_size, char **string, grub_size_t *string_size)
+{
+ asn1_node strasn;
+ grub_int32_t result;
+ char *choice;
+ grub_int32_t choice_size = 0;
+ grub_int32_t tmp_size = 0;
+ grub_err_t err = GRUB_ERR_NONE;
+
+ result = asn1_create_element (grub_gnutls_pkix_asn, "PKIX1.DirectoryString", &strasn);
+ if (result != ASN1_SUCCESS)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not create ASN.1 structure for certificate: %s",
+ asn1_strerror (result));
+
+ result = asn1_der_decoding2 (&strasn, der, &der_size, ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+ if (result != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "could not parse DER for DirectoryString: %s", asn1_error);
+ goto cleanup;
+ }
+
+ choice = grub_asn1_allocate_and_read (strasn, "", "DirectoryString choice", &choice_size);
+ if (choice == NULL)
+ {
+ err = grub_errno;
+ goto cleanup;
+ }
+
+ if (grub_strncmp ("utf8String", choice, choice_size) == 0)
+ {
+ result = asn1_read_value (strasn, "utf8String", NULL, &tmp_size);
+ if (result != ASN1_MEM_ERROR)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading size of UTF-8 string: %s",
+ asn1_strerror (result));
+ goto cleanup_choice;
+ }
+ }
+ else if (grub_strncmp ("printableString", choice, choice_size) == 0)
+ {
+ result = asn1_read_value (strasn, "printableString", NULL, &tmp_size);
+ if (result != ASN1_MEM_ERROR)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading size of printableString: %s",
+ asn1_strerror (result));
+ goto cleanup_choice;
+ }
+ }
+ else
+ {
+ err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "only UTF-8 and printable DirectoryStrings are supported, got %s",
+ choice);
+ goto cleanup_choice;
+ }
+
+ /* Read size does not include trailing NUL. */
+ tmp_size++;
+
+ *string = grub_malloc (tmp_size);
+ if (*string == NULL)
+ {
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "cannot allocate memory for DirectoryString contents");
+ goto cleanup_choice;
+ }
+
+ result = asn1_read_value (strasn, choice, *string, &tmp_size);
+ if (result != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading out %s in DirectoryString: %s",
+ choice, asn1_strerror (result));
+ grub_free (*string);
+ *string = NULL;
+ goto cleanup_choice;
+ }
+
+ *string_size = tmp_size + 1;
+ (*string)[tmp_size] = '\0';
+
+ cleanup_choice:
+ grub_free (choice);
+ cleanup:
+ asn1_delete_structure (&strasn);
+
+ return err;
+}
+
+/*
+ * TBSCertificate ::= SEQUENCE {
+ * version [0] EXPLICIT Version DEFAULT v1,
+ * ...
+ *
+ * Version ::= INTEGER { v1(0), v2(1), v3(2) }
+ */
+static grub_err_t
+check_version (asn1_node certificate, grub_x509_cert_t *results)
+{
+ grub_int32_t rc;
+ const char *name = "tbsCertificate.version";
+ grub_uint8_t version;
+ grub_int32_t len = sizeof (version);
+
+ rc = asn1_read_value (certificate, name, &version, &len);
+
+ /* Require version 3. */
+ if (rc != ASN1_SUCCESS || len != 1)
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading certificate version");
+
+ if (version != 0x02)
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "invalid x509 certificate version, expected v3 (0x02), got 0x%02x.",
+ version);
+
+ results->version = version;
+
+ return GRUB_ERR_NONE;
+}
+
+/* we extract only the CN and issuer. */
+static grub_err_t
+read_name (asn1_node asn, const char *name_path, char **name, grub_size_t *name_size)
+{
+ grub_int32_t seq_components, set_components;
+ grub_int32_t result;
+ grub_int32_t i, j;
+ char *top_path, *set_path, *type_path, *val_path;
+ char type[GRUB_MAX_OID_LEN];
+ grub_int32_t type_len = sizeof (type);
+ grub_int32_t string_size = 0;
+ char *string_der;
+ grub_err_t err;
+
+ *name = NULL;
+
+ top_path = grub_xasprintf ("%s.rdnSequence", name_path);
+ if (top_path == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not allocate memory for %s name parsing path", name_path);
+
+ result = asn1_number_of_elements (asn, top_path, &seq_components);
+ if (result != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error counting name components: %s",
+ asn1_strerror (result));
+ goto cleanup;
+ }
+
+ for (i = 1; i <= seq_components; i++)
+ {
+ set_path = grub_xasprintf ("%s.?%d", top_path, i);
+ if (set_path == NULL)
+ {
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not allocate memory for %s name set parsing path",
+ name_path);
+ goto cleanup_set;
+ }
+ /* This brings us, hopefully, to a set. */
+ result = asn1_number_of_elements (asn, set_path, &set_components);
+ if (result != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "error counting name sub-components components (element %d): %s",
+ i, asn1_strerror (result));
+ goto cleanup_set;
+ }
+ for (j = 1; j <= set_components; j++)
+ {
+ type_path = grub_xasprintf ("%s.?%d.?%d.type", top_path, i, j);
+ if (type_path == NULL)
+ {
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not allocate memory for %s name component type path",
+ name_path);
+ goto cleanup_set;
+ }
+ type_len = sizeof (type);
+ result = asn1_read_value (asn, type_path, type, &type_len);
+ if (result != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading %s name component type: %s",
+ name_path, asn1_strerror (result));
+ goto cleanup_type;
+ }
+
+ if (grub_strncmp (type, commonName_oid, type_len) != 0)
+ {
+ grub_free (type_path);
+ continue;
+ }
+
+ val_path = grub_xasprintf ("%s.?%d.?%d.value", top_path, i, j);
+ if (val_path == NULL)
+ {
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not allocate memory for %s name component value path",
+ name_path);
+ goto cleanup_type;
+ }
+
+ string_der = grub_asn1_allocate_and_read (asn, val_path, name_path, &string_size);
+ if (string_der == NULL)
+ {
+ err = grub_errno;
+ goto cleanup_val_path;
+ }
+
+ err = decode_string (string_der, string_size, name, name_size);
+ if (err)
+ goto cleanup_string;
+
+ grub_free (string_der);
+ grub_free (type_path);
+ grub_free (val_path);
+ break;
+ }
+
+ grub_free (set_path);
+ if (*name)
+ break;
+ }
+
+ grub_free (top_path);
+
+ return GRUB_ERR_NONE;
+
+ cleanup_string:
+ grub_free (string_der);
+ cleanup_val_path:
+ grub_free (val_path);
+ cleanup_type:
+ grub_free (type_path);
+ cleanup_set:
+ grub_free (set_path);
+ cleanup:
+ grub_free (top_path);
+
+ return err;
+}
+
+/* Verify the Key Usage extension. We require the Digital Signature usage. */
+static grub_err_t
+verify_key_usage (grub_uint8_t *value, grub_int32_t value_size)
+{
+ asn1_node usageasn;
+ grub_int32_t result;
+ grub_err_t err = GRUB_ERR_NONE;
+ grub_uint8_t usage = 0xff;
+ grub_int32_t usage_size = sizeof (usage_size);
+
+ result = asn1_create_element (grub_gnutls_pkix_asn, "PKIX1.KeyUsage", &usageasn);
+ if (result != ASN1_SUCCESS)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not create ASN.1 structure for key usage");
+
+ result = asn1_der_decoding2 (&usageasn, value, &value_size,
+ ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+ if (result != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "error parsing DER for Key Usage: %s", asn1_error);
+ goto cleanup;
+ }
+
+ result = asn1_read_value (usageasn, "", &usage, &usage_size);
+ if (result != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading Key Usage value: %s",
+ asn1_strerror (result));
+ goto cleanup;
+ }
+
+ if (!(usage & digitalSignatureUsage))
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "key usage (0x%x) missing Digital Signature usage", usage);
+ goto cleanup;
+ }
+
+ cleanup:
+ asn1_delete_structure (&usageasn);
+
+ return err;
+}
+
+/*
+ * BasicConstraints ::= SEQUENCE {
+ * cA BOOLEAN DEFAULT FALSE,
+ * pathLenConstraint INTEGER (0..MAX) OPTIONAL }
+ */
+static grub_err_t
+verify_basic_constraints (grub_uint8_t *value, grub_int32_t value_size)
+{
+ asn1_node basicasn;
+ grub_int32_t result;
+ grub_err_t err = GRUB_ERR_NONE;
+ char cA[6]; /* FALSE or TRUE. */
+ grub_int32_t cA_size = sizeof (cA);
+
+ result = asn1_create_element (grub_gnutls_pkix_asn, "PKIX1.BasicConstraints", &basicasn);
+ if (result != ASN1_SUCCESS)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not create ASN.1 structure for Basic Constraints");
+
+ result = asn1_der_decoding2 (&basicasn, value, &value_size,
+ ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+ if (result != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "error parsing DER for Basic Constraints: %s", asn1_error);
+ goto cleanup;
+ }
+
+ result = asn1_read_value (basicasn, "cA", cA, &cA_size);
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ {
+ /* Not present, default is False, so this is OK. */
+ err = GRUB_ERR_NONE;
+ goto cleanup;
+ }
+ else if (result != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading Basic Constraints cA value: %s",
+ asn1_strerror (result));
+ goto cleanup;
+ }
+
+ /* The certificate must not be a CA certificate. */
+ if (grub_strncmp ("FALSE", cA, cA_size) != 0)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "unexpected CA value: %s", cA);
+ goto cleanup;
+ }
+
+ cleanup:
+ asn1_delete_structure (&basicasn);
+
+ return err;
+}
+
+/*
+ * Verify the Extended Key Usage extension. We require the Code Signing usage.
+ *
+ * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
+ *
+ * KeyPurposeId ::= OBJECT IDENTIFIER
+ */
+static grub_err_t
+verify_extended_key_usage (grub_uint8_t *value, grub_int32_t value_size)
+{
+ asn1_node extendedasn;
+ grub_int32_t result, count, i = 0;
+ grub_err_t err = GRUB_ERR_NONE;
+ char usage[GRUB_MAX_OID_LEN], name[3];
+ grub_int32_t usage_size = sizeof (usage);
+
+ result = asn1_create_element (grub_gnutls_pkix_asn, "PKIX1.ExtKeyUsageSyntax", &extendedasn);
+ if (result != ASN1_SUCCESS)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not create ASN.1 structure for Extended Key Usage");
+
+ result = asn1_der_decoding2 (&extendedasn, value, &value_size,
+ ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+ if (result != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "error parsing DER for Extended Key Usage: %s", asn1_error);
+ goto cleanup;
+ }
+
+ /* If EKUs are present, it checks the presents of Code Signing usage. */
+ result = asn1_number_of_elements (extendedasn, "", &count);
+ if (result != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error counting number of Extended Key Usages: %s",
+ asn1_strerror (result));
+ goto cleanup;
+ }
+
+ for (i = 1; i < count + 1; i++)
+ {
+ grub_memset (name, 0, sizeof (name));
+ grub_snprintf (name, sizeof (name), "?%d", i);
+ result = asn1_read_value (extendedasn, name, usage, &usage_size);
+ if (result != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading Extended Key Usage: %s",
+ asn1_strerror (result));
+ goto cleanup;
+ }
+
+ if (grub_strncmp (codeSigningUsage_oid, usage, usage_size) == 0)
+ goto cleanup;
+ }
+
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "extended key usage missing Code Signing usage");
+
+ cleanup:
+ asn1_delete_structure (&extendedasn);
+
+ return err;
+}
+
+/*
+ * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
+ *
+ * Extension ::= SEQUENCE {
+ * extnID OBJECT IDENTIFIER,
+ * critical BOOLEAN DEFAULT FALSE,
+ * extnValue OCTET STRING
+ * -- contains the DER encoding of an ASN.1 value
+ * -- corresponding to the extension type identified
+ * -- by extnID
+ * }
+ *
+ * A certificate must:
+ * - contain the Digital Signature usage
+ * - not be a CA
+ * - contain no extended usages, or contain the Code Signing extended usage
+ * - not contain any other critical extensions (RFC 5280 s 4.2)
+ */
+static grub_err_t
+verify_extensions (asn1_node cert)
+{
+ grub_int32_t result;
+ grub_int32_t ext, num_extensions = 0;
+ grub_int32_t usage_present = 0, constraints_present = 0, extended_usage_present = 0;
+ char *oid_path, *critical_path, *value_path;
+ char extnID[GRUB_MAX_OID_LEN];
+ grub_int32_t extnID_size;
+ grub_err_t err;
+ char critical[6]; /* We get either "TRUE" or "FALSE". */
+ grub_int32_t critical_size;
+ grub_uint8_t *value;
+ grub_int32_t value_size;
+
+ result = asn1_number_of_elements (cert, "tbsCertificate.extensions", &num_extensions);
+ if (result != ASN1_SUCCESS)
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "error counting number of extensions: %s",
+ asn1_strerror (result));
+
+ if (num_extensions < 2)
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "insufficient number of extensions for certificate, need at least 2, got %d",
+ num_extensions);
+
+ for (ext = 1; ext <= num_extensions; ext++)
+ {
+ oid_path = grub_xasprintf ("tbsCertificate.extensions.?%d.extnID", ext);
+ if (oid_path == NULL)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error extension OID path is empty");
+ return err;
+ }
+
+ extnID_size = sizeof (extnID);
+ result = asn1_read_value (cert, oid_path, extnID, &extnID_size);
+ if (result != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading extension OID: %s",
+ asn1_strerror (result));
+ goto cleanup_oid_path;
+ }
+
+ critical_path = grub_xasprintf ("tbsCertificate.extensions.?%d.critical", ext);
+ if (critical_path == NULL)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error critical path is empty");
+ goto cleanup_oid_path;
+ }
+
+ critical_size = sizeof (critical);
+ result = asn1_read_value (cert, critical_path, critical, &critical_size);
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ critical[0] = '\0';
+ else if (result != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading extension criticality: %s",
+ asn1_strerror (result));
+ goto cleanup_critical_path;
+ }
+
+ value_path = grub_xasprintf ("tbsCertificate.extensions.?%d.extnValue", ext);
+ if (value_path == NULL)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error extnValue path is empty");
+ goto cleanup_critical_path;
+ }
+
+ value = grub_asn1_allocate_and_read (cert, value_path,
+ "certificate extension value", &value_size);
+ if (value == NULL)
+ {
+ err = grub_errno;
+ goto cleanup_value_path;
+ }
+
+ /*
+ * Now we must see if we recognise the OID. If we have an unrecognised
+ * critical extension we MUST bail.
+ */
+ if (grub_strncmp (keyUsage_oid, extnID, extnID_size) == 0)
+ {
+ err = verify_key_usage (value, value_size);
+ if (err != GRUB_ERR_NONE)
+ goto cleanup_value;
+
+ usage_present++;
+ }
+ else if (grub_strncmp (basicConstraints_oid, extnID, extnID_size) == 0)
+ {
+ err = verify_basic_constraints (value, value_size);
+ if (err != GRUB_ERR_NONE)
+ goto cleanup_value;
+
+ constraints_present++;
+ }
+ else if (grub_strncmp (extendedKeyUsage_oid, extnID, extnID_size) == 0)
+ {
+ err = verify_extended_key_usage (value, value_size);
+ if (err != GRUB_ERR_NONE)
+ goto cleanup_value;
+
+ extended_usage_present++;
+ }
+ else if (grub_strncmp ("TRUE", critical, critical_size) == 0)
+ {
+ /*
+ * Per the RFC, we must not process a certificate with a critical
+ * extension we do not understand.
+ */
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "unhandled critical x509 extension with OID %s", extnID);
+ goto cleanup_value;
+ }
+
+ grub_free (value);
+ grub_free (value_path);
+ grub_free (critical_path);
+ grub_free (oid_path);
+ }
+
+ if (usage_present != 1)
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "unexpected number of Key Usage extensions - expected 1, got %d",
+ usage_present);
+
+ if (constraints_present != 1)
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "unexpected number of basic constraints extensions - expected 1, got %d",
+ constraints_present);
+
+ if (extended_usage_present > 1)
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "unexpected number of Extended Key Usage extensions - expected 0 or 1, got %d",
+ extended_usage_present);
+
+ return GRUB_ERR_NONE;
+
+ cleanup_value:
+ grub_free (value);
+ cleanup_value_path:
+ grub_free (value_path);
+ cleanup_critical_path:
+ grub_free (critical_path);
+ cleanup_oid_path:
+ grub_free (oid_path);
+
+ return err;
+}
+
+static void
+add_cert_fingerprint (const void *data, const grub_size_t data_size,
+ grub_x509_cert_t *const cert)
+{
+ /* Add SHA256 hash of certificate. */
+ grub_crypto_hash ((gcry_md_spec_t *) &_gcry_digest_spec_sha256,
+ &cert->fingerprint[GRUB_FINGERPRINT_SHA256], data, data_size);
+ /* Add SHA384 hash of certificate. */
+ grub_crypto_hash ((gcry_md_spec_t *) &_gcry_digest_spec_sha384,
+ &cert->fingerprint[GRUB_FINGERPRINT_SHA384], data, data_size);
+ /* Add SHA512 hash of certificate. */
+ grub_crypto_hash ((gcry_md_spec_t *) &_gcry_digest_spec_sha512,
+ &cert->fingerprint[GRUB_FINGERPRINT_SHA512], data, data_size);
+}
+
+/*
+ * Parse a certificate whose DER-encoded form is in @data, of size @data_size.
+ * Return the results in @results, which must point to an allocated x509
+ * certificate.
+ */
+grub_err_t
+grub_x509_cert_parse (const void *data, grub_size_t data_size, grub_x509_cert_t *results)
+{
+ grub_int32_t result = 0;
+ asn1_node cert;
+ grub_err_t err;
+ grub_int32_t tmp_size;
+ grub_int32_t size = (grub_int32_t) data_size;
+
+ if (data_size > GRUB_UINT_MAX)
+ return grub_error (GRUB_ERR_OUT_OF_RANGE,
+ "cannot parse a certificate where data size > GRUB_UINT_MAX");
+
+ result = asn1_create_element (grub_gnutls_pkix_asn, "PKIX1.Certificate", &cert);
+ if (result != ASN1_SUCCESS)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not create ASN.1 structure for certificate: %s",
+ asn1_strerror (result));
+
+ result = asn1_der_decoding2 (&cert, data, &size, ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+ if (result != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "could not parse DER for certificate: %s", asn1_error);
+ goto cleanup;
+ }
+
+ /*
+ * TBSCertificate ::= SEQUENCE {
+ * version [0] EXPLICIT Version DEFAULT v1
+ */
+ err = check_version (cert, results);
+ if (err != GRUB_ERR_NONE)
+ goto cleanup;
+
+ /*
+ * serialNumber CertificateSerialNumber,
+ *
+ * CertificateSerialNumber ::= INTEGER
+ */
+ results->serial = grub_asn1_allocate_and_read (cert, "tbsCertificate.serialNumber",
+ "certificate serial number", &tmp_size);
+ if (results->serial == NULL)
+ {
+ err = grub_errno;
+ goto cleanup;
+ }
+ /*
+ * It's safe to cast the signed int to an unsigned here, we know
+ * length is non-negative.
+ */
+ results->serial_len = tmp_size;
+
+ /*
+ * signature AlgorithmIdentifier,
+ *
+ * We don't load the signature or issuer at the moment,
+ * as we don't attempt x509 verification.
+ */
+
+ /*
+ * validity Validity,
+ *
+ * Validity ::= SEQUENCE {
+ * notBefore Time,
+ * notAfter Time }
+ *
+ * We can't validate this reasonably, we have no true time source on several
+ * platforms. For now we do not parse them.
+ */
+
+ /*
+ * issuer Name,
+ *
+ * This is an X501 name, we parse out just the issuer.
+ */
+ err = read_name (cert, "tbsCertificate.issuer", &results->issuer, &results->issuer_len);
+ if (err != GRUB_ERR_NONE)
+ goto cleanup_serial;
+
+ /*
+ * subject Name,
+ *
+ * This is an X501 name, we parse out just the CN.
+ */
+ err = read_name (cert, "tbsCertificate.subject", &results->subject, &results->subject_len);
+ if (err != GRUB_ERR_NONE)
+ goto cleanup_issuer;
+
+ /*
+ * TBSCertificate ::= SEQUENCE {
+ * ...
+ * subjectPublicKeyInfo SubjectPublicKeyInfo,
+ * ...
+ */
+ err = grub_x509_read_subject_public_key (cert, results);
+ if (err != GRUB_ERR_NONE)
+ goto cleanup_name;
+
+ /*
+ * TBSCertificate ::= SEQUENCE {
+ * ...
+ * extensions [3] EXPLICIT Extensions OPTIONAL
+ * -- If present, version MUST be v3
+ * }
+ */
+ err = verify_extensions (cert);
+ if (err != GRUB_ERR_NONE)
+ goto cleanup_mpis;
+
+ /*
+ * We do not read or check the signature on the certificate:
+ * as discussed we do not try to validate the certificate but trust
+ * it implictly.
+ */
+ asn1_delete_structure (&cert);
+
+ /* Add the fingerprint of the certificate. */
+ add_cert_fingerprint (data, data_size, results);
+
+ return GRUB_ERR_NONE;
+
+ cleanup_mpis:
+ _gcry_mpi_release (results->mpis[GRUB_RSA_PK_MODULUS]);
+ _gcry_mpi_release (results->mpis[GRUB_RSA_PK_EXPONENT]);
+ cleanup_name:
+ grub_free (results->subject);
+ cleanup_issuer:
+ grub_free (results->issuer);
+ cleanup_serial:
+ grub_free (results->serial);
+ cleanup:
+ asn1_delete_structure (&cert);
+
+ return err;
+}
+
+/*
+ * Release all the storage associated with the x509 certificate. If the caller
+ * dynamically allocated the certificate, it must free it. The caller is also
+ * responsible for maintenance of the linked list.
+ */
+void
+grub_x509_cert_release (grub_x509_cert_t *cert)
+{
+ grub_free (cert->issuer);
+ grub_free (cert->subject);
+ grub_free (cert->serial);
+ _gcry_mpi_release (cert->mpis[GRUB_RSA_PK_MODULUS]);
+ _gcry_mpi_release (cert->mpis[GRUB_RSA_PK_EXPONENT]);
+}
diff --git a/grub-core/commands/bli.c b/grub-core/commands/bli.c
index 298c5f70a..38f52f87a 100644
--- a/grub-core/commands/bli.c
+++ b/grub-core/commands/bli.c
@@ -28,6 +28,7 @@
#include
#include
#include
+#include
#include
GRUB_MOD_LICENSE ("GPLv3+");
@@ -127,12 +128,34 @@ set_loader_device_part_uuid (void)
return status;
}
+static grub_err_t
+set_loader_active_pcr_banks (void)
+{
+ grub_efi_uint32_t active_pcr_banks;
+ char *active_pcr_banks_str;
+ grub_err_t status;
+
+ active_pcr_banks = grub_tpm2_active_pcr_banks();
+ active_pcr_banks_str = grub_xasprintf ("0x%08x", active_pcr_banks);
+ if (active_pcr_banks_str == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate active PCR banks string"));
+
+ status = grub_efi_set_variable_to_string ("LoaderTpm2ActivePcrBanks",
+ &bli_vendor_guid,
+ active_pcr_banks_str,
+ GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ GRUB_EFI_VARIABLE_RUNTIME_ACCESS);
+ grub_free (active_pcr_banks_str);
+ return status;
+}
+
GRUB_MOD_INIT (bli)
{
grub_efi_set_variable_to_string ("LoaderInfo", &bli_vendor_guid, PACKAGE_STRING,
GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS |
GRUB_EFI_VARIABLE_RUNTIME_ACCESS);
set_loader_device_part_uuid ();
+ set_loader_active_pcr_banks ();
/* No error here is critical, other than being logged */
grub_print_error ();
}
diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c
new file mode 100644
index 000000000..4133d3111
--- /dev/null
+++ b/grub-core/commands/blsuki.c
@@ -0,0 +1,1503 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2025 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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef GRUB_MACHINE_EFI
+#include
+#include
+#include
+#endif
+
+#ifdef GRUB_MACHINE_EMU
+#include
+#define GRUB_BOOT_DEVICE "/boot"
+#else
+#define GRUB_BOOT_DEVICE ""
+#endif
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define GRUB_BLS_CONFIG_PATH "/loader/entries/"
+#define GRUB_UKI_CONFIG_PATH "/EFI/Linux"
+
+#define BLS_EXT_LEN (sizeof (".conf") - 1)
+#define UKI_EXT_LEN (sizeof (".efi") - 1)
+
+/*
+ * It is highly unlikely to ever receive a large amount of keyval pairs. A
+ * limit of 10000 is more than enough.
+ */
+#define BLSUKI_KEYVALS_MAX 10000
+/*
+ * The only sections we read are ".cmdline" and ".osrel". The ".cmdline"
+ * section has a size limit of 4096 and it would be very unlikely for the size
+ * of the ".osrel" section to be 5 times larger than 4096.
+ */
+#define UKI_SECTION_SIZE_MAX (5 * 4096)
+
+enum blsuki_cmd_type
+ {
+ BLSUKI_BLS_CMD,
+ BLSUKI_UKI_CMD,
+ };
+
+static const struct grub_arg_option bls_opt[] =
+ {
+ {"path", 'p', 0, "Specify path to find BLS entries.", N_("DIR"), ARG_TYPE_PATHNAME},
+ {"enable-fallback", 'f', 0, "Fallback to the default BLS path if --path fails to find BLS entries.", 0, ARG_TYPE_NONE},
+ {"show-default", 'd', 0, "Allow the default BLS entry to be added to the GRUB menu.", 0, ARG_TYPE_NONE},
+ {"show-non-default", 'n', 0, "Allow the non-default BLS entries to be added to the GRUB menu.", 0, ARG_TYPE_NONE},
+ {"entry", 'e', 0, "Allow specific BLS entries to be added to the GRUB menu.", N_("FILE"), ARG_TYPE_FILE},
+ {0, 0, 0, 0, 0, 0}
+ };
+
+#ifdef GRUB_MACHINE_EFI
+static const struct grub_arg_option uki_opt[] =
+ {
+ {"path", 'p', 0, N_("Specify path to find UKI entries."), N_("DIR"), ARG_TYPE_PATHNAME},
+ {"enable-fallback", 'f', 0, "Fallback to the default BLS path if --path fails to find UKI entries.", 0, ARG_TYPE_NONE},
+ {"show-default", 'd', 0, N_("Allow the default UKI entry to be added to the GRUB menu."), 0, ARG_TYPE_NONE},
+ {"show-non-default", 'n', 0, N_("Allow the non-default UKI entries to be added to the GRUB menu."), 0, ARG_TYPE_NONE},
+ {"entry", 'e', 0, N_("Allow specific UKI entries to be added to the GRUB menu."), N_("FILE"), ARG_TYPE_FILE},
+ {0, 0, 0, 0, 0, 0}
+ };
+#endif
+
+struct keyval
+{
+ const char *key;
+ char *val;
+};
+
+struct read_entry_info
+{
+ const char *devid;
+ const char *dirname;
+ enum blsuki_cmd_type cmd_type;
+ grub_file_t file;
+};
+
+struct find_entry_info
+{
+ const char *dirname;
+ const char *devid;
+ grub_device_t dev;
+ grub_fs_t fs;
+};
+
+static grub_blsuki_entry_t *entries = NULL;
+
+#define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries)
+
+/*
+ * BLS appears to make paths relative to the filesystem that snippets are
+ * on, not /. Attempt to cope.
+ */
+static char *blsuki_update_boot_device (char *tmp)
+{
+#ifdef GRUB_MACHINE_EMU
+ static int separate_boot = -1;
+ char *ret;
+
+ if (separate_boot != -1)
+ goto probed;
+
+ separate_boot = 0;
+
+ ret = grub_make_system_path_relative_to_its_root (GRUB_BOOT_DEVICE);
+
+ if (ret != NULL && ret[0] == '\0')
+ separate_boot = 1;
+
+ probed:
+ if (separate_boot == 0)
+ return tmp;
+#endif
+
+ return grub_stpcpy (tmp, GRUB_BOOT_DEVICE);
+}
+
+/*
+ * This function will add a new keyval pair to a list of keyvals stored in the
+ * entry parameter.
+ */
+static grub_err_t
+blsuki_add_keyval (grub_blsuki_entry_t *entry, char *key, char *val)
+{
+ char *k, *v;
+ struct keyval **kvs, *kv;
+ grub_size_t size;
+ int new_n = entry->nkeyvals + 1;
+
+ if (new_n > BLSUKI_KEYVALS_MAX)
+ return grub_error (GRUB_ERR_BAD_NUMBER, "too many keyval pairs");
+
+ if (entry->keyvals_size == 0)
+ {
+ size = sizeof (struct keyval *);
+ kvs = grub_malloc (size);
+ if (kvs == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't allocate space for BLS key values");
+
+ entry->keyvals = kvs;
+ entry->keyvals_size = size;
+ }
+ else if (entry->keyvals_size < new_n * sizeof (struct keyval *))
+ {
+ size = entry->keyvals_size * 2;
+ kvs = grub_realloc (entry->keyvals, size);
+ if (kvs == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't reallocate space for BLS key values");
+
+ entry->keyvals = kvs;
+ entry->keyvals_size = size;
+ }
+
+ kv = grub_malloc (sizeof (struct keyval));
+ if (kv == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value");
+
+ k = grub_strdup (key);
+ if (k == NULL)
+ {
+ grub_free (kv);
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value");
+ }
+
+ v = grub_strdup (val);
+ if (v == NULL)
+ {
+ grub_free (k);
+ grub_free (kv);
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value");
+ }
+
+ kv->key = k;
+ kv->val = v;
+ entry->keyvals[entry->nkeyvals] = kv;
+ entry->nkeyvals = new_n;
+
+ return GRUB_ERR_NONE;
+}
+
+/*
+ * Find the value of the key named by keyname. If there are allowed to be
+ * more than one, pass a pointer set to -1 to the last parameter the first
+ * time, and pass the same pointer through each time after, and it'll return
+ * them in sorted order.
+ */
+static char *
+blsuki_get_val (grub_blsuki_entry_t *entry, const char *keyname, int *last)
+{
+ int idx, start = (last != NULL) ? (*last + 1) : 0;
+ struct keyval *kv = NULL;
+ char *ret = NULL;
+
+ for (idx = start; idx < entry->nkeyvals; idx++)
+ {
+ kv = entry->keyvals[idx];
+
+ if (grub_strcmp (keyname, kv->key) == 0)
+ {
+ ret = kv->val;
+ break;
+ }
+ }
+
+ if (last != NULL)
+ {
+ if (idx == entry->nkeyvals)
+ *last = -1;
+ else
+ *last = idx;
+ }
+
+ return ret;
+}
+
+/*
+ * Add a new grub_blsuki_entry_t struct to the entries list and sort it's
+ * position on the list.
+ */
+static grub_err_t
+blsuki_add_entry (grub_blsuki_entry_t *entry)
+{
+ grub_blsuki_entry_t *e, *last = NULL;
+ int rc;
+
+ if (entries == NULL)
+ {
+ grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename);
+ entries = entry;
+ return GRUB_ERR_NONE;
+ }
+
+ FOR_BLSUKI_ENTRIES (e)
+ {
+ rc = filevercmp (entry->filename, e->filename);
+ if (rc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("duplicate file: `%s'"), entry->filename);
+
+ if (rc > 0)
+ {
+ grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename);
+ grub_list_push (GRUB_AS_LIST_P (&e), GRUB_AS_LIST (entry));
+ if (entry->next == entries)
+ {
+ entries = entry;
+ entry->prev = NULL;
+ }
+ else if (last != NULL)
+ last->next = entry;
+
+ return GRUB_ERR_NONE;
+ }
+ last = e;
+ }
+
+ if (last != NULL)
+ {
+ grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename);
+ last->next = entry;
+ entry->prev = &last;
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+/*
+ * This function parses each line of a BLS config file to obtain the key value
+ * pairs that will be used to setup the GRUB menu entries. The key value pair
+ * will be stored in a list in the entry parameter.
+ */
+static grub_err_t
+bls_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry)
+{
+ grub_err_t err = GRUB_ERR_NONE;
+
+ for (;;)
+ {
+ char *line, *key, *val;
+
+ line = grub_file_getline (f);
+ if (line == NULL)
+ break;
+
+ key = grub_strtok_r (line, " \t", &val);
+ if (key == NULL)
+ {
+ grub_free (line);
+ break;
+ }
+ if (*key == '#')
+ {
+ grub_free (line);
+ continue;
+ }
+
+ while (*val == ' ' || *val == '\t')
+ val++;
+
+ if (*val == '\0')
+ {
+ grub_free (line);
+ break;
+ }
+
+ err = blsuki_add_keyval (entry, key, val);
+ grub_free (line);
+ if (err != GRUB_ERR_NONE)
+ break;
+ }
+
+ return err;
+}
+
+#ifdef GRUB_MACHINE_EFI
+/*
+ * This function searches for the .cmdline, .osrel, and .linux sections of a
+ * UKI. We only need to store the data for the .cmdline and .osrel sections,
+ * but we also need to verify that the .linux section exists.
+ */
+static grub_err_t
+uki_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry)
+{
+ struct grub_msdos_image_header *dos = NULL;
+ struct grub_pe_image_header *pe = NULL;
+ grub_off_t section_offset = 0;
+ struct grub_pe32_section_table *section = NULL;
+ struct grub_pe32_coff_header *coff_header = NULL;
+ char *val = NULL;
+ char *key = NULL;
+ const char *target[] = {".cmdline", ".osrel", ".linux", NULL};
+ bool has_linux = false;
+ grub_err_t err = GRUB_ERR_NONE;
+
+ dos = grub_zalloc (sizeof (*dos));
+ if (dos == NULL)
+ return grub_errno;
+ if (grub_file_read (f, dos, sizeof (*dos)) < (grub_ssize_t) sizeof (*dos))
+ {
+ err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read UKI image header");
+ goto finish;
+ }
+ if (dos->msdos_magic != GRUB_PE32_MAGIC)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "plain image kernel is not supported");
+ goto finish;
+ }
+
+ grub_dprintf ("blsuki", "PE/COFF header @ %08x\n", dos->pe_image_header_offset);
+ pe = grub_zalloc (sizeof (*pe));
+ if (pe == NULL)
+ {
+ err = grub_errno;
+ goto finish;
+ }
+ if (grub_file_seek (f, dos->pe_image_header_offset) == (grub_off_t) -1 ||
+ grub_file_read (f, pe, sizeof (*pe)) != sizeof (*pe))
+ {
+ err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read COFF image header");
+ goto finish;
+ }
+ if (pe->optional_header.magic != GRUB_PE32_NATIVE_MAGIC)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "non-native image not supported");
+ goto finish;
+ }
+
+ coff_header = &(pe->coff_header);
+ section_offset = dos->pe_image_header_offset + sizeof (*pe);
+
+ for (int i = 0; i < coff_header->num_sections; i++)
+ {
+ section = grub_zalloc (sizeof (*section));
+ if (section == NULL)
+ {
+ err = grub_errno;
+ goto finish;
+ }
+
+ if (grub_file_seek (f, section_offset) == (grub_off_t) -1 ||
+ grub_file_read (f, section, sizeof (*section)) != sizeof (*section))
+ {
+ err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read section header");
+ goto finish;
+ }
+
+ key = grub_strndup (section->name, 8);
+ if (key == NULL)
+ {
+ err = grub_errno;
+ goto finish;
+ }
+
+ for (int j = 0; target[j] != NULL; j++)
+ {
+ if (grub_strcmp (key, target[j]) == 0)
+ {
+ /*
+ * We don't need to read the contents of the .linux PE section, but we
+ * should verify that the section exists.
+ */
+ if (grub_strcmp (key, ".linux") == 0)
+ {
+ has_linux = true;
+ break;
+ }
+
+ if (section->raw_data_size > UKI_SECTION_SIZE_MAX)
+ {
+ err = grub_error (GRUB_ERR_BAD_NUMBER, "UKI section size is larger than expected");
+ goto finish;
+ }
+
+ val = grub_zalloc (section->raw_data_size);
+ if (val == NULL)
+ {
+ err = grub_errno;
+ goto finish;
+ }
+
+ if (grub_file_seek (f, section->raw_data_offset) == (grub_off_t) -1 ||
+ grub_file_read (f, val, section->raw_data_size) != (grub_ssize_t) section->raw_data_size)
+ {
+ err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read section");
+ goto finish;
+ }
+
+ err = blsuki_add_keyval (entry, key, val);
+ if (err != GRUB_ERR_NONE)
+ goto finish;
+
+ break;
+ }
+ }
+
+ section_offset += sizeof (*section);
+ grub_free (section);
+ grub_free (val);
+ grub_free (key);
+ section = NULL;
+ val = NULL;
+ key = NULL;
+ }
+
+ if (has_linux == false)
+ err = grub_error (GRUB_ERR_NO_KERNEL, "UKI is missing the '.linux' section");
+
+ finish:
+ grub_free (dos);
+ grub_free (pe);
+ grub_free (section);
+ grub_free (val);
+ grub_free (key);
+ return err;
+}
+
+/*
+ * This function obtains the keyval pairs when the .osrel data is input into
+ * the osrel_ptr parameter and returns the keyval pair. Since we are using
+ * grub_strtok_r(), the osrel_ptr will be updated to the following line of
+ * osrel. This function returns NULL when it reaches the end of osrel.
+ */
+static char *
+uki_read_osrel (char **osrel_ptr, char **val_ret)
+{
+ char *key, *val;
+ grub_size_t val_size;
+
+ for (;;)
+ {
+ key = grub_strtok_r (NULL, "\n\r", osrel_ptr);
+ if (key == NULL)
+ return NULL;
+
+ /* Remove leading white space */
+ while (*key == ' ' || *key == '\t')
+ key++;
+
+ /* Skip commented lines */
+ if (*key == '#')
+ continue;
+
+ /* Split key/value */
+ key = grub_strtok_r (key, "=", &val);
+ if (key == NULL || *val == '\0')
+ continue;
+
+ /* Remove quotes from value */
+ val_size = grub_strlen (val);
+ if ((*val == '\"' && val[val_size - 1] == '\"') ||
+ (*val == '\'' && val[val_size - 1] == '\''))
+ {
+ val[val_size - 1] = '\0';
+ val++;
+ }
+
+ *val_ret = val;
+ break;
+ }
+
+ return key;
+}
+#endif
+
+/*
+ * If a file hasn't already been opened, this function opens a BLS config file
+ * or UKI and initializes entry data before parsing keyvals and adding the entry
+ * to the list of BLS or UKI entries.
+ */
+static int
+blsuki_read_entry (const char *filename,
+ const struct grub_dirhook_info *dirhook_info __attribute__ ((__unused__)),
+ void *data)
+{
+ grub_size_t path_len = 0, ext_len = 0, filename_len;
+ grub_err_t err = GRUB_ERR_NONE;
+ char *p = NULL;
+ const char *ext = NULL;
+ grub_file_t f = NULL;
+ enum grub_file_type file_type = 0;
+ grub_blsuki_entry_t *entry;
+ struct read_entry_info *info = (struct read_entry_info *) data;
+
+ grub_dprintf ("blsuki", "filename: \"%s\"\n", filename);
+
+ filename_len = grub_strlen (filename);
+
+ if (info->cmd_type == BLSUKI_BLS_CMD)
+ {
+ ext = ".conf";
+ ext_len = BLS_EXT_LEN;
+ file_type = GRUB_FILE_TYPE_CONFIG;
+ }
+#ifdef GRUB_MACHINE_EFI
+ else if (info->cmd_type == BLSUKI_UKI_CMD)
+ {
+ ext = ".efi";
+ ext_len = UKI_EXT_LEN;
+ file_type = GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE;
+ }
+#endif
+
+ if (info->file != NULL)
+ f = info->file;
+ else
+ {
+ if (filename_len < ext_len ||
+ grub_strcmp (filename + filename_len - ext_len, ext) != 0)
+ return 0;
+
+ p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname, filename);
+ f = grub_file_open (p, file_type);
+ grub_free (p);
+ if (f == NULL)
+ goto finish;
+ }
+
+ entry = grub_zalloc (sizeof (*entry));
+ if (entry == NULL)
+ goto finish;
+
+ /*
+ * If a file is opened before this function, the filename may have a path.
+ * Since the filename is used for the ID of the GRUB menu entry, we can
+ * remove the path.
+ */
+ if (info->file != NULL)
+ {
+ char *slash;
+
+ slash = grub_strrchr (filename, '/');
+ if (slash != NULL)
+ path_len = slash - filename + 1;
+ }
+ filename_len -= path_len;
+
+ entry->filename = grub_strndup (filename + path_len, filename_len);
+ if (entry->filename == NULL)
+ {
+ grub_free (entry);
+ goto finish;
+ }
+
+ entry->dirname = grub_strdup (info->dirname);
+ if (entry->dirname == NULL)
+ {
+ grub_free (entry);
+ goto finish;
+ }
+
+ entry->devid = grub_strdup (info->devid);
+ if (entry->devid == NULL)
+ {
+ grub_free (entry);
+ goto finish;
+ }
+
+ if (info->cmd_type == BLSUKI_BLS_CMD)
+ err = bls_parse_keyvals (f, entry);
+#ifdef GRUB_MACHINE_EFI
+ else if (info->cmd_type == BLSUKI_UKI_CMD)
+ err = uki_parse_keyvals (f, entry);
+#endif
+
+ if (err == GRUB_ERR_NONE)
+ blsuki_add_entry (entry);
+ else
+ grub_free (entry);
+
+ finish:
+ if (f != NULL)
+ grub_file_close (f);
+
+ return 0;
+}
+
+/*
+ * This function returns a list of values that had the same key in the BLS
+ * config file or UKI. The number of entries in this list is returned by the len
+ * parameter.
+ */
+static char **
+blsuki_make_list (grub_blsuki_entry_t *entry, const char *key, int *len)
+{
+ int last = -1;
+ char *val;
+ int nlist = 0;
+ char **list;
+
+ list = grub_zalloc (sizeof (char *));
+ if (list == NULL)
+ return NULL;
+
+ while (1)
+ {
+ char **new;
+
+ /*
+ * Since the same key might appear more than once, the 'last' variable
+ * starts at -1 and increments to indicate the last index in the list
+ * we obtained from blsuki_get_val().
+ */
+ val = blsuki_get_val (entry, key, &last);
+ if (val == NULL)
+ break;
+
+ new = grub_realloc (list, (nlist + 2) * sizeof (char *));
+ if (new == NULL)
+ break;
+
+ list = new;
+ list[nlist++] = val;
+ list[nlist] = NULL;
+ }
+
+ if (nlist == 0)
+ {
+ grub_free (list);
+ return NULL;
+ }
+
+ if (len != NULL)
+ *len = nlist;
+
+ return list;
+}
+
+/*
+ * This function appends a field to the end of a buffer. If the field given is
+ * an enviornmental variable, it gets the value stored for that variable and
+ * appends that to the buffer instead.
+ */
+static char *
+blsuki_field_append (bool is_env_var, char *buffer, const char *start, const char *end)
+{
+ char *tmp;
+ const char *field;
+ grub_size_t size = 0;
+
+ tmp = grub_strndup (start, end - start + 1);
+ if (tmp == NULL)
+ return NULL;
+
+ field = tmp;
+
+ if (is_env_var == true)
+ {
+ field = grub_env_get (tmp);
+ if (field == NULL)
+ return buffer;
+ }
+
+ if (grub_add (grub_strlen (field), 1, &size))
+ return NULL;
+
+ if (buffer == NULL)
+ buffer = grub_zalloc (size);
+ else
+ {
+ if (grub_add (size, grub_strlen (buffer), &size))
+ return NULL;
+
+ buffer = grub_realloc (buffer, size);
+ }
+
+ if (buffer == NULL)
+ return NULL;
+
+ tmp = buffer + grub_strlen (buffer);
+ tmp = grub_stpcpy (tmp, field);
+
+ if (is_env_var == true)
+ tmp = grub_stpcpy (tmp, " ");
+
+ return buffer;
+}
+
+/*
+ * This function takes a value string, checks for environmental variables, and
+ * returns the value string with all environmental variables replaced with the
+ * value stored in the variable.
+ */
+static char *
+blsuki_expand_val (const char *value)
+{
+ char *buffer = NULL;
+ const char *start = value;
+ const char *end = value;
+ bool is_env_var = false;
+
+ if (value == NULL)
+ return NULL;
+
+ while (*value != '\0')
+ {
+ if (*value == '$')
+ {
+ if (start != end)
+ {
+ buffer = blsuki_field_append (is_env_var, buffer, start, end);
+ if (buffer == NULL)
+ return NULL;
+ }
+
+ is_env_var = true;
+ start = value + 1;
+ }
+ else if (is_env_var == true)
+ {
+ if (grub_isalnum (*value) == 0 && *value != '_')
+ {
+ buffer = blsuki_field_append (is_env_var, buffer, start, end);
+ is_env_var = false;
+ start = value;
+ if (*start == ' ')
+ start++;
+ }
+ }
+
+ end = value;
+ value++;
+ }
+
+ if (start != end)
+ {
+ buffer = blsuki_field_append (is_env_var, buffer, start, end);
+ if (buffer == NULL)
+ return NULL;
+ }
+
+ return buffer;
+}
+
+/*
+ * This function returns a string with the command to load a linux kernel with
+ * kernel command-line options based on what was specified in the BLS config
+ * file.
+ */
+static char *
+bls_get_linux (grub_blsuki_entry_t *entry)
+{
+ char *linux_path;
+ char *linux_cmd = NULL;
+ char *options = NULL;
+ char *tmp;
+ grub_size_t size;
+
+ linux_path = blsuki_get_val (entry, "linux", NULL);
+ options = blsuki_expand_val (blsuki_get_val (entry, "options", NULL));
+
+ if (grub_add (sizeof ("linux " GRUB_BOOT_DEVICE), grub_strlen (linux_path), &size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size");
+ goto finish;
+ }
+
+ if (options != NULL)
+ {
+ if (grub_add (size, grub_strlen (options), &size) ||
+ grub_add (size, 1, &size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size");
+ goto finish;
+ }
+ }
+
+ linux_cmd = grub_malloc (size);
+ if (linux_cmd == NULL)
+ goto finish;
+
+ tmp = linux_cmd;
+ tmp = grub_stpcpy (tmp, "linux ");
+ tmp = blsuki_update_boot_device (tmp);
+ tmp = grub_stpcpy (tmp, linux_path);
+ if (options != NULL)
+ {
+ tmp = grub_stpcpy (tmp, " ");
+ tmp = grub_stpcpy (tmp, options);
+ }
+
+ tmp = grub_stpcpy (tmp, "\n");
+
+ finish:
+ grub_free (options);
+ return linux_cmd;
+}
+
+/*
+ * This function returns a string with the command to load all initrds for a
+ * linux kernel image based on the list provided by the BLS config file.
+ */
+static char *
+bls_get_initrd (grub_blsuki_entry_t *entry)
+{
+ char **initrd_list;
+ char *initrd_cmd = NULL;
+ char *tmp;
+ grub_size_t size;
+ int i;
+
+ initrd_list = blsuki_make_list (entry, "initrd", NULL);
+ if (initrd_list != NULL)
+ {
+ size = sizeof ("initrd");
+
+ for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++)
+ {
+ if (grub_add (size, sizeof (" " GRUB_BOOT_DEVICE) - 1, &size) ||
+ grub_add (size, grub_strlen (initrd_list[i]), &size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size");
+ goto finish;
+ }
+ }
+
+ if (grub_add (size, 1, &size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size");
+ goto finish;
+ }
+
+ initrd_cmd = grub_malloc (size);
+ if (initrd_cmd == NULL)
+ goto finish;
+
+ tmp = grub_stpcpy (initrd_cmd, "initrd");
+ for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++)
+ {
+ grub_dprintf ("blsuki", "adding initrd %s\n", initrd_list[i]);
+ tmp = grub_stpcpy (tmp, " ");
+ tmp = blsuki_update_boot_device (tmp);
+ tmp = grub_stpcpy (tmp, initrd_list[i]);
+ }
+ tmp = grub_stpcpy (tmp, "\n");
+ }
+
+ finish:
+ grub_free (initrd_list);
+ return initrd_cmd;
+}
+
+/*
+ * This function returns a string with the command to load a device tree blob
+ * from the BLS config file.
+ */
+static char *
+bls_get_devicetree (grub_blsuki_entry_t *entry)
+{
+ char *dt_path;
+ char *dt_cmd = NULL;
+ char *tmp;
+ grub_size_t size;
+
+ dt_path = blsuki_expand_val (blsuki_get_val (entry, "devicetree", NULL));
+ if (dt_path != NULL)
+ {
+ if (grub_add (sizeof ("devicetree " GRUB_BOOT_DEVICE), grub_strlen (dt_path), &size) ||
+ grub_add (size, 1, &size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating device tree buffer size");
+ return NULL;
+ }
+
+ dt_cmd = grub_malloc (size);
+ if (dt_cmd == NULL)
+ return NULL;
+
+ tmp = dt_cmd;
+ tmp = grub_stpcpy (dt_cmd, "devicetree ");
+ tmp = blsuki_update_boot_device (tmp);
+ tmp = grub_stpcpy (tmp, dt_path);
+ tmp = grub_stpcpy (tmp, "\n");
+ }
+
+ return dt_cmd;
+}
+
+/*
+ * This function puts together all of the commands generated from the contents
+ * of the BLS config file and creates a new entry in the GRUB boot menu.
+ */
+static void
+bls_create_entry (grub_blsuki_entry_t *entry)
+{
+ int argc = 0;
+ const char **argv = NULL;
+ char *title = NULL;
+ char *linux_path = NULL;
+ char *linux_cmd = NULL;
+ char *initrd_cmd = NULL;
+ char *dt_cmd = NULL;
+ char *id = entry->filename;
+ grub_size_t id_len;
+ char *hotkey = NULL;
+ char *users = NULL;
+ char **classes = NULL;
+ char **args = NULL;
+ char *src = NULL;
+ int i;
+ grub_size_t size;
+ bool blsuki_save_default;
+
+ linux_path = blsuki_get_val (entry, "linux", NULL);
+ if (linux_path == NULL)
+ {
+ grub_dprintf ("blsuki", "Skipping file %s with no 'linux' key.\n", entry->filename);
+ goto finish;
+ }
+
+ id_len = grub_strlen (id);
+ if (id_len >= BLS_EXT_LEN && grub_strcmp (id + id_len - BLS_EXT_LEN, ".conf") == 0)
+ id[id_len - BLS_EXT_LEN] = '\0';
+
+ title = blsuki_get_val (entry, "title", NULL);
+ hotkey = blsuki_get_val (entry, "grub_hotkey", NULL);
+ users = blsuki_expand_val (blsuki_get_val (entry, "grub_users", NULL));
+ classes = blsuki_make_list (entry, "grub_class", NULL);
+ args = blsuki_make_list (entry, "grub_arg", &argc);
+
+ argc++;
+ if (grub_mul (argc + 1, sizeof (char *), &size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected creating argv list"));
+ goto finish;
+ }
+
+ argv = grub_malloc (size);
+ if (argv == NULL)
+ goto finish;
+
+ argv[0] = (title != NULL) ? title : linux_path;
+ for (i = 1; i < argc; i++)
+ argv[i] = args[i - 1];
+ argv[argc] = NULL;
+
+ linux_cmd = bls_get_linux (entry);
+ if (linux_cmd == NULL)
+ goto finish;
+
+ initrd_cmd = bls_get_initrd (entry);
+ if (grub_errno != GRUB_ERR_NONE)
+ goto finish;
+
+ dt_cmd = bls_get_devicetree (entry);
+ if (grub_errno != GRUB_ERR_NONE)
+ goto finish;
+
+ blsuki_save_default = grub_env_get_bool ("blsuki_save_default", false);
+ src = grub_xasprintf ("%s%s%s%s",
+ blsuki_save_default ? "savedefault\n" : "",
+ linux_cmd, initrd_cmd ? initrd_cmd : "",
+ dt_cmd ? dt_cmd : "");
+
+ grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, entry);
+
+ finish:
+ grub_free (linux_cmd);
+ grub_free (dt_cmd);
+ grub_free (initrd_cmd);
+ grub_free (classes);
+ grub_free (args);
+ grub_free (argv);
+ grub_free (src);
+}
+
+#ifdef GRUB_MACHINE_EFI
+/*
+ * This function puts together the section data received from the UKI and
+ * generates a new entry in the GRUB boot menu.
+ */
+static void
+uki_create_entry (grub_blsuki_entry_t *entry)
+{
+ const char **argv = NULL;
+ char *id = entry->filename;
+ char *title = NULL;
+ char *options = NULL;
+ char *osrel, *osrel_line;
+ char *key = NULL;
+ char *value = NULL;
+ char *src = NULL;
+ bool blsuki_save_default;
+
+ /*
+ * Although .osrel is listed as optional in the UKI specification, the .osrel
+ * section is needed to generate the GRUB menu entry title.
+ */
+ osrel = blsuki_get_val (entry, ".osrel", NULL);
+ if (osrel == NULL)
+ {
+ grub_dprintf ("blsuki", "Skipping file %s with no '.osrel' key.\n", entry->filename);
+ goto finish;
+ }
+
+ osrel_line = osrel;
+ while ((key = uki_read_osrel (&osrel_line, &value)) != NULL)
+ {
+ if (grub_strcmp ("PRETTY_NAME", key) == 0)
+ {
+ title = value;
+ break;
+ }
+ }
+
+ options = blsuki_get_val (entry, ".cmdline", NULL);
+
+ argv = grub_zalloc (2 * sizeof (char *));
+ if (argv == NULL)
+ goto finish;
+ argv[0] = title;
+
+ blsuki_save_default = grub_env_get_bool ("blsuki_save_default", false);
+ src = grub_xasprintf ("%schainloader (%s)%s/%s%s%s\n",
+ blsuki_save_default ? "savedefault\n" : "",
+ entry->devid, entry->dirname,
+ entry->filename,
+ (options != NULL) ? " " : "",
+ (options != NULL) ? options : "");
+
+ grub_normal_add_menu_entry (1, argv, NULL, id, NULL, NULL, NULL, src, 0, entry);
+
+ finish:
+ grub_free (argv);
+ grub_free (src);
+ grub_free (options);
+ grub_free (osrel);
+}
+#endif
+
+/*
+ * This function fills a find_entry_info struct passed in by the info parameter.
+ * If the dirname or devid parameters are set to NULL, the dirname and devid
+ * fields in the info parameter will be set to default values. If info already
+ * has a value in the dev fields, we can compare it to the value passed in by
+ * the devid parameter or the default devid to see if we need to open a new
+ * device.
+ */
+static grub_err_t
+blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, const char *devid, enum blsuki_cmd_type cmd_type)
+{
+ grub_device_t dev;
+ grub_fs_t fs;
+
+ if (info == NULL)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "info parameter is not set");
+
+ if (devid == NULL)
+ {
+ if (cmd_type == BLSUKI_BLS_CMD)
+ {
+#ifdef GRUB_MACHINE_EMU
+ devid = "host";
+#else
+ devid = grub_env_get ("root");
+#endif
+ }
+#ifdef GRUB_MACHINE_EFI
+ else if (cmd_type == BLSUKI_UKI_CMD)
+ {
+ grub_efi_loaded_image_t *image = grub_efi_get_loaded_image (grub_efi_image_handle);
+
+ if (image == NULL)
+ return grub_error (GRUB_ERR_BAD_DEVICE, N_("unable to find boot device"));
+ devid = grub_efidisk_get_device_name (image->device_handle);
+ }
+#endif
+ if (devid == NULL)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable '%s' isn't set"), "root");
+ }
+
+ /* Check that we aren't closing and opening the same device. */
+ if (info->dev != NULL && grub_strcmp (info->devid, devid) != 0)
+ {
+ grub_device_close (info->dev);
+ info->dev = NULL;
+ }
+ /* If we are using the same device, then we can skip this step and only set the directory. */
+ if (info->dev == NULL)
+ {
+ grub_dprintf ("blsuki", "opening %s\n", devid);
+ dev = grub_device_open (devid);
+ if (dev == NULL)
+ return grub_errno;
+
+ grub_dprintf ("blsuki", "probing fs\n");
+ fs = grub_fs_probe (dev);
+ if (fs == NULL)
+ {
+ grub_device_close (dev);
+ return grub_errno;
+ }
+
+ info->devid = devid;
+ info->dev = dev;
+ info->fs = fs;
+ }
+
+ info->dirname = dirname;
+
+ return GRUB_ERR_NONE;
+}
+
+/*
+ * This function searches for BLS config files and UKIs based on the data in the
+ * info parameter. If the fallback option is enabled, the default location will
+ * be checked for BLS config files or UKIs if the first attempt fails.
+ */
+static grub_err_t
+blsuki_find_entry (struct find_entry_info *info, bool enable_fallback, enum blsuki_cmd_type cmd_type)
+{
+ struct read_entry_info read_entry_info;
+ char *default_dir = NULL;
+ const char *cmd_dir = NULL;
+ char *tmp;
+ grub_size_t default_size;
+ grub_fs_t dir_fs = NULL;
+ grub_device_t dir_dev = NULL;
+ bool fallback = false;
+ int r;
+
+ do
+ {
+ read_entry_info.file = NULL;
+ read_entry_info.dirname = info->dirname;
+
+ grub_dprintf ("blsuki", "scanning dir: %s\n", info->dirname);
+ dir_dev = info->dev;
+ dir_fs = info->fs;
+ read_entry_info.devid = info->devid;
+ read_entry_info.cmd_type = cmd_type;
+
+ r = dir_fs->fs_dir (dir_dev, read_entry_info.dirname, blsuki_read_entry,
+ &read_entry_info);
+ if (r != 0)
+ {
+ grub_dprintf ("blsuki", "blsuki_read_entry returned error\n");
+ grub_errno = GRUB_ERR_NONE;
+ }
+
+ /*
+ * If we aren't able to find BLS entries in the directory given by info->dirname,
+ * we can fallback to the default location of "/loader/entries/" and see if we
+ * can find the files there. If we can't find UKI entries, fallback to
+ * "/EFI/Linux" on the EFI system partition.
+ */
+ if (entries == NULL && fallback == false && enable_fallback == true)
+ {
+ if (cmd_type == BLSUKI_BLS_CMD)
+ cmd_dir = GRUB_BLS_CONFIG_PATH;
+#ifdef GRUB_MACHINE_EFI
+ else if (cmd_type == BLSUKI_UKI_CMD)
+ cmd_dir = GRUB_UKI_CONFIG_PATH;
+#endif
+
+ default_size = sizeof (GRUB_BOOT_DEVICE) + grub_strlen (cmd_dir);
+ default_dir = grub_malloc (default_size);
+ if (default_dir == NULL)
+ return grub_errno;
+
+ tmp = blsuki_update_boot_device (default_dir);
+ tmp = grub_stpcpy (tmp, cmd_dir);
+
+ blsuki_set_find_entry_info (info, default_dir, info->devid, cmd_type);
+ grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n",
+ read_entry_info.dirname, info->dirname);
+ fallback = true;
+ }
+ else
+ fallback = false;
+ }
+ while (fallback == true);
+
+ grub_free (default_dir);
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+blsuki_load_entries (char *path, bool enable_fallback, enum blsuki_cmd_type cmd_type)
+{
+ grub_size_t len, ext_len = 0;
+ static grub_err_t r;
+ const char *devid = NULL;
+ char *dir = NULL;
+ char *default_dir = NULL;
+ char *tmp;
+ const char *cmd_dir = NULL;
+ grub_size_t dir_size;
+ const char *ext = NULL;
+ struct find_entry_info info = {
+ .dev = NULL,
+ .fs = NULL,
+ .dirname = NULL,
+ };
+ struct read_entry_info rei = {
+ .devid = NULL,
+ .dirname = NULL,
+ .cmd_type = cmd_type,
+ };
+
+ if (path != NULL)
+ {
+ if (cmd_type == BLSUKI_BLS_CMD)
+ {
+ ext = ".conf";
+ ext_len = BLS_EXT_LEN;
+ }
+#ifdef GRUB_MACHINE_EFI
+ else if (cmd_type == BLSUKI_UKI_CMD)
+ {
+ ext = ".efi";
+ ext_len = UKI_EXT_LEN;
+ }
+#endif
+
+ len = grub_strlen (path);
+ if (len >= ext_len && grub_strcmp (path + len - ext_len, ext) == 0)
+ {
+ rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG);
+ if (rei.file == NULL)
+ return grub_errno;
+
+ /* blsuki_read_entry() closes the file. */
+ return blsuki_read_entry (path, NULL, &rei);
+ }
+ else if (path[0] == '(')
+ {
+ devid = path + 1;
+
+ dir = grub_strchr (path, ')');
+ if (dir == NULL)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid file name `%s'"), path);
+
+ *dir = '\0';
+
+ /* Check if there is more than the devid in the path. */
+ if (dir + 1 < path + len)
+ dir = dir + 1;
+ }
+ else if (path[0] == '/')
+ dir = path;
+ }
+
+ if (dir == NULL)
+ {
+ if (cmd_type == BLSUKI_BLS_CMD)
+ cmd_dir = GRUB_BLS_CONFIG_PATH;
+#ifdef GRUB_MACHINE_EFI
+ else if (cmd_type == BLSUKI_UKI_CMD)
+ cmd_dir = GRUB_UKI_CONFIG_PATH;
+#endif
+
+ dir_size = sizeof (GRUB_BOOT_DEVICE) + grub_strlen (cmd_dir);
+ default_dir = grub_malloc (dir_size);
+ if (default_dir == NULL)
+ return grub_errno;
+
+ tmp = blsuki_update_boot_device (default_dir);
+ tmp = grub_stpcpy (tmp, cmd_dir);
+ dir = default_dir;
+ }
+
+ r = blsuki_set_find_entry_info (&info, dir, devid, cmd_type);
+ if (r == GRUB_ERR_NONE)
+ r = blsuki_find_entry (&info, enable_fallback, cmd_type);
+
+ if (info.dev != NULL)
+ grub_device_close (info.dev);
+
+ grub_free (default_dir);
+ return r;
+}
+
+static bool
+blsuki_is_default_entry (const char *def_entry, grub_blsuki_entry_t *entry, int idx)
+{
+ const char *title;
+ const char *def_entry_end;
+ long def_idx;
+
+ if (def_entry == NULL || *def_entry == '\0')
+ return false;
+
+ if (grub_strcmp (def_entry, entry->filename) == 0)
+ return true;
+
+ title = blsuki_get_val (entry, "title", NULL);
+
+ if (title != NULL && grub_strcmp (def_entry, title) == 0)
+ return true;
+
+ def_idx = grub_strtol (def_entry, &def_entry_end, 0);
+
+ /* Clear grub_errno so we can plug the leak. */
+ grub_errno = GRUB_ERR_NONE;
+
+ if (*def_entry_end != '\0' || def_idx < 0 || def_idx > GRUB_INT_MAX)
+ return false;
+
+ if ((int) def_idx == idx)
+ return true;
+
+ return false;
+}
+
+/*
+ * This function creates a GRUB boot menu entry for each BLS or UKI entry in
+ * the entries list.
+ */
+static grub_err_t
+blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id, enum blsuki_cmd_type cmd_type)
+{
+ const char *def_entry = NULL;
+ grub_blsuki_entry_t *entry = NULL;
+ int idx = 0;
+
+ def_entry = grub_env_get ("default");
+
+ FOR_BLSUKI_ENTRIES(entry)
+ {
+ if (entry->visible == true)
+ {
+ idx++;
+ continue;
+ }
+ if ((show_default == true && blsuki_is_default_entry (def_entry, entry, idx) == true) ||
+ (show_non_default == true && blsuki_is_default_entry (def_entry, entry, idx) == false) ||
+ (entry_id != NULL && grub_strcmp (entry_id, entry->filename) == 0))
+ {
+ if (cmd_type == BLSUKI_BLS_CMD)
+ bls_create_entry (entry);
+#ifdef GRUB_MACHINE_EFI
+ else if (cmd_type == BLSUKI_UKI_CMD)
+ uki_create_entry (entry);
+#endif
+ entry->visible = true;
+ }
+
+ idx++;
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+blsuki_cmd (grub_extcmd_context_t ctxt, enum blsuki_cmd_type cmd_type)
+{
+ grub_err_t err;
+ struct grub_arg_list *state = ctxt->state;
+ char *path = NULL;
+ char *entry_id = NULL;
+ bool enable_fallback = false;
+ bool show_default = false;
+ bool show_non_default = false;
+ bool all = true;
+ entries = NULL;
+
+ if (state[0].set)
+ path = state[0].arg;
+ if (state[1].set)
+ enable_fallback = true;
+ if (state[2].set)
+ {
+ show_default = true;
+ all = false;
+ }
+ if (state[3].set)
+ {
+ show_non_default = true;
+ all = false;
+ }
+ if (state[4].set)
+ {
+ entry_id = state[4].arg;
+ all = false;
+ }
+ if (all == true)
+ {
+ show_default = true;
+ show_non_default = true;
+ }
+
+ err = blsuki_load_entries (path, enable_fallback, cmd_type);
+ if (err != GRUB_ERR_NONE)
+ return err;
+
+ return blsuki_create_entries (show_default, show_non_default, entry_id, cmd_type);
+}
+
+static grub_err_t
+grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc,
+ char **args __attribute__ ((unused)))
+{
+ if (argc != 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unexpected argument(s) found, see --help"));
+ return blsuki_cmd (ctxt, BLSUKI_BLS_CMD);
+}
+
+static grub_extcmd_t bls_cmd;
+
+#ifdef GRUB_MACHINE_EFI
+static grub_err_t
+grub_cmd_uki (grub_extcmd_context_t ctxt, int argc,
+ char **args __attribute__ ((unused)))
+{
+ if (argc != 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unexpected argument(s) found, see --help"));
+ return blsuki_cmd (ctxt, BLSUKI_UKI_CMD);
+}
+
+static grub_extcmd_t uki_cmd;
+#endif
+
+GRUB_MOD_INIT(blsuki)
+{
+ bls_cmd = grub_register_extcmd ("blscfg", grub_cmd_blscfg, 0,
+ N_("[-p|--path] DIR [-f|--enable-fallback] [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"),
+ N_("Import Boot Loader Specification snippets."),
+ bls_opt);
+#ifdef GRUB_MACHINE_EFI
+ uki_cmd = grub_register_extcmd ("uki", grub_cmd_uki, 0,
+ N_("[-p|--path] DIR [-f|--enable-fallback] [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"),
+ N_("Import Unified Kernel Images"), uki_opt);
+#endif
+}
+
+GRUB_MOD_FINI(blsuki)
+{
+ grub_unregister_extcmd (bls_cmd);
+#ifdef GRUB_MACHINE_EFI
+ grub_unregister_extcmd (uki_cmd);
+#endif
+}
diff --git a/grub-core/commands/efi/lsefi.c b/grub-core/commands/efi/lsefi.c
index 7b8316d41..f4c10392d 100644
--- a/grub-core/commands/efi/lsefi.c
+++ b/grub-core/commands/efi/lsefi.c
@@ -129,6 +129,7 @@ grub_cmd_lsefi (grub_command_t cmd __attribute__ ((unused)),
}
+ grub_free (handles);
return 0;
}
diff --git a/grub-core/commands/efi/lsefisystab.c b/grub-core/commands/efi/lsefisystab.c
index ffb24fc3b..015d36fe0 100644
--- a/grub-core/commands/efi/lsefisystab.c
+++ b/grub-core/commands/efi/lsefisystab.c
@@ -47,6 +47,7 @@ static const struct guid_mapping guid_mappings[] =
{ GRUB_EFI_HOB_LIST_GUID, "HOB LIST"},
{ GRUB_EFI_IMAGE_SECURITY_DATABASE_GUID, "IMAGE EXECUTION INFORMATION"},
{ GRUB_EFI_LZMA_CUSTOM_DECOMPRESS_GUID, "LZMA CUSTOM DECOMPRESS"},
+ { GRUB_EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMORY ATTRIBUTES TABLE"},
{ GRUB_EFI_MEMORY_TYPE_INFORMATION_GUID, "MEMORY TYPE INFO"},
{ GRUB_EFI_MPS_TABLE_GUID, "MPS"},
{ GRUB_EFI_RT_PROPERTIES_TABLE_GUID, "RT PROPERTIES"},
@@ -54,6 +55,7 @@ static const struct guid_mapping guid_mappings[] =
{ GRUB_EFI_SMBIOS_TABLE_GUID, "SMBIOS"},
{ GRUB_EFI_SMBIOS3_TABLE_GUID, "SMBIOS3"},
{ GRUB_EFI_SYSTEM_RESOURCE_TABLE_GUID, "SYSTEM RESOURCE TABLE"},
+ { GRUB_EFI_TCG2_FINAL_EVENTS_TABLE_GUID, "TCG2 FINAL EVENTS TABLE"},
{ GRUB_EFI_TIANO_CUSTOM_DECOMPRESS_GUID, "TIANO CUSTOM DECOMPRESS"},
{ GRUB_EFI_TSC_FREQUENCY_GUID, "TSC FREQUENCY"},
};
diff --git a/grub-core/commands/efi/tpm.c b/grub-core/commands/efi/tpm.c
index cbac69866..1c4906403 100644
--- a/grub-core/commands/efi/tpm.c
+++ b/grub-core/commands/efi/tpm.c
@@ -39,6 +39,7 @@ static grub_uint8_t grub_tpm_version;
static grub_int8_t tpm1_present = -1;
static grub_int8_t tpm2_present = -1;
+static grub_efi_int64_t tpm2_active_pcr_banks = -1;
static grub_efi_boolean_t
grub_tpm1_present (grub_efi_tpm_protocol_t *tpm)
@@ -112,6 +113,7 @@ grub_tpm_handle_find (grub_efi_handle_t *tpm_handle,
grub_tpm_version = 1;
*protocol_version = 1;
grub_dprintf ("tpm", "TPM handle Found, version: 1\n");
+ grub_free (handles);
return 1;
}
@@ -124,6 +126,7 @@ grub_tpm_handle_find (grub_efi_handle_t *tpm_handle,
grub_tpm_version = 2;
*protocol_version = 2;
grub_dprintf ("tpm", "TPM handle Found, version: 2\n");
+ grub_free (handles);
return 1;
}
@@ -332,3 +335,49 @@ grub_tpm_present (void)
return grub_tpm2_present (tpm);
}
}
+
+grub_uint32_t
+grub_tpm2_active_pcr_banks (void)
+{
+ EFI_TCG2_BOOT_SERVICE_CAPABILITY caps;
+ grub_efi_handle_t tpm_handle;
+ grub_efi_uint8_t protocol_version;
+ grub_efi_tpm2_protocol_t *tpm;
+ grub_efi_uint32_t active_pcr_banks;
+ grub_efi_status_t status;
+
+ if (tpm2_active_pcr_banks >= 0)
+ return (grub_uint32_t) tpm2_active_pcr_banks;
+
+ if (!grub_tpm_handle_find (&tpm_handle, &protocol_version))
+ return (grub_uint32_t) (tpm2_active_pcr_banks = 0);
+
+ if (protocol_version == 1)
+ return (grub_uint32_t) (tpm2_active_pcr_banks = 0); /* We report TPM2 status. */
+
+ tpm = grub_efi_open_protocol (tpm_handle, &tpm2_guid,
+ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (tpm == NULL)
+ {
+ grub_dprintf ("tpm", "Cannot open TPM2 protocol\n");
+ return (grub_uint32_t) (tpm2_active_pcr_banks = 0);
+ }
+
+ if (!grub_tpm2_present (tpm))
+ return (grub_uint32_t) (tpm2_active_pcr_banks = 0);
+
+ caps.Size = (grub_uint8_t) sizeof (caps);
+ status = tpm->get_capability (tpm, &caps);
+ if (status != GRUB_EFI_SUCCESS)
+ return (grub_uint32_t) (tpm2_active_pcr_banks = 0);
+ if (caps.StructureVersion.Major < 1 ||
+ (caps.StructureVersion.Major == 1 && caps.StructureVersion.Minor < 1))
+ /* There's a working TPM2 but without querying protocol, let userspace figure it out. */
+ return (grub_uint32_t) (tpm2_active_pcr_banks = GRUB_UINT_MAX);
+
+ status = tpm->get_active_pcr_banks (tpm, &active_pcr_banks);
+ if (status != GRUB_EFI_SUCCESS)
+ return (grub_uint32_t) (tpm2_active_pcr_banks = 0); /* Assume none available if the call fails. */
+
+ return (grub_uint32_t) (tpm2_active_pcr_banks = active_pcr_banks);
+}
diff --git a/grub-core/commands/extcmd.c b/grub-core/commands/extcmd.c
index c236be13a..7f8cb3f67 100644
--- a/grub-core/commands/extcmd.c
+++ b/grub-core/commands/extcmd.c
@@ -139,6 +139,9 @@ grub_register_extcmd_lockdown (const char *name, grub_extcmd_func_t func,
void
grub_unregister_extcmd (grub_extcmd_t ext)
{
+ if (ext == NULL)
+ return;
+
grub_unregister_command (ext->cmd);
grub_free (ext);
}
diff --git a/grub-core/commands/ieee1275/ibmvtpm.c b/grub-core/commands/ieee1275/ibmvtpm.c
index 4958b04a9..665d90931 100644
--- a/grub-core/commands/ieee1275/ibmvtpm.c
+++ b/grub-core/commands/ieee1275/ibmvtpm.c
@@ -27,67 +27,20 @@
#include
#include
-static int
-ibmvtpm_2hash_ext_log (grub_uint8_t pcrindex,
- grub_uint32_t eventtype,
- const char *description,
- grub_size_t description_size,
- void *buf, grub_size_t size)
-{
- struct tpm_2hash_ext_log
- {
- struct grub_ieee1275_common_hdr common;
- grub_ieee1275_cell_t method;
- grub_ieee1275_cell_t ihandle;
- grub_ieee1275_cell_t size;
- grub_ieee1275_cell_t buf;
- grub_ieee1275_cell_t description_size;
- grub_ieee1275_cell_t description;
- grub_ieee1275_cell_t eventtype;
- grub_ieee1275_cell_t pcrindex;
- grub_ieee1275_cell_t catch_result;
- grub_ieee1275_cell_t rc;
- };
- struct tpm_2hash_ext_log args;
-
- INIT_IEEE1275_COMMON (&args.common, "call-method", 8, 2);
- args.method = (grub_ieee1275_cell_t) "2hash-ext-log";
- args.ihandle = grub_ieee1275_tpm_ihandle;
- args.pcrindex = pcrindex;
- args.eventtype = eventtype;
- args.description = (grub_ieee1275_cell_t) description;
- args.description_size = description_size;
- args.buf = (grub_ieee1275_cell_t) buf;
- args.size = (grub_ieee1275_cell_t) size;
-
- if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
- return -1;
-
- /*
- * catch_result is set if firmware does not support 2hash-ext-log
- * rc is GRUB_IEEE1275_CELL_FALSE (0) on failure
- */
- if ((args.catch_result) || args.rc == GRUB_IEEE1275_CELL_FALSE)
- return -1;
-
- return 0;
-}
-
static grub_err_t
tpm2_log_event (unsigned char *buf, grub_size_t size, grub_uint8_t pcr,
const char *description)
{
static int error_displayed = 0;
- int rc;
+ grub_err_t err;
- rc = ibmvtpm_2hash_ext_log (pcr, EV_IPL,
- description, grub_strlen(description) + 1,
- buf, size);
- if (rc && !error_displayed)
+ err = grub_ieee1275_ibmvtpm_2hash_ext_log (pcr, EV_IPL,
+ description, grub_strlen(description) + 1,
+ buf, size);
+ if (err != GRUB_ERR_NONE && !error_displayed)
{
error_displayed++;
- return grub_error (GRUB_ERR_BAD_DEVICE,
- "2HASH-EXT-LOG failed: Firmware is likely too old.\n");
+ return err;
}
return GRUB_ERR_NONE;
diff --git a/grub-core/commands/legacycfg.c b/grub-core/commands/legacycfg.c
index a768fa6e8..7d9f9eb3c 100644
--- a/grub-core/commands/legacycfg.c
+++ b/grub-core/commands/legacycfg.c
@@ -143,7 +143,7 @@ legacy_file (const char *filename)
args[0] = oldname;
grub_normal_add_menu_entry (1, args, NULL, NULL, "legacy",
NULL, NULL,
- entrysrc, 0);
+ entrysrc, 0, NULL);
grub_free (args);
entrysrc[0] = 0;
grub_free (oldname);
@@ -204,7 +204,7 @@ legacy_file (const char *filename)
}
args[0] = entryname;
grub_normal_add_menu_entry (1, args, NULL, NULL, NULL,
- NULL, NULL, entrysrc, 0);
+ NULL, NULL, entrysrc, 0, NULL);
grub_free (args);
}
diff --git a/grub-core/commands/memtools.c b/grub-core/commands/memtools.c
index ae0a9bec3..f9ba45efa 100644
--- a/grub-core/commands/memtools.c
+++ b/grub-core/commands/memtools.c
@@ -53,6 +53,18 @@ grub_cmd_lsfreemem (grub_command_t cmd __attribute__ ((unused)),
return 0;
}
+static grub_err_t
+grub_cmd_lsmemregions (grub_command_t cmd __attribute__ ((unused)),
+ int argc __attribute__ ((unused)),
+ char **args __attribute__ ((unused)))
+
+{
+#ifndef GRUB_MACHINE_EMU
+ grub_mm_dump_regions ();
+#endif
+
+ return 0;
+}
static grub_err_t
grub_cmd_stress_big_allocs (grub_command_t cmd __attribute__ ((unused)),
@@ -132,7 +144,7 @@ grub_cmd_stress_big_allocs (grub_command_t cmd __attribute__ ((unused)),
return GRUB_ERR_NONE;
}
-static grub_command_t cmd_lsmem, cmd_lsfreemem, cmd_sba;
+static grub_command_t cmd_lsmem, cmd_lsfreemem, cmd_lsmemregions, cmd_sba;
GRUB_MOD_INIT (memtools)
{
@@ -140,6 +152,8 @@ GRUB_MOD_INIT (memtools)
0, N_("List free and allocated memory blocks."));
cmd_lsfreemem = grub_register_command ("lsfreemem", grub_cmd_lsfreemem,
0, N_("List free memory blocks."));
+ cmd_lsmemregions = grub_register_command ("lsmemregions", grub_cmd_lsmemregions,
+ 0, N_("List memory regions."));
cmd_sba = grub_register_command ("stress_big_allocs", grub_cmd_stress_big_allocs,
0, N_("Stress test large allocations."));
}
@@ -148,5 +162,6 @@ GRUB_MOD_FINI (memtools)
{
grub_unregister_command (cmd_lsmem);
grub_unregister_command (cmd_lsfreemem);
+ grub_unregister_command (cmd_lsmemregions);
grub_unregister_command (cmd_sba);
}
diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c
index 720e6d8ea..5e1318f17 100644
--- a/grub-core/commands/menuentry.c
+++ b/grub-core/commands/menuentry.c
@@ -78,7 +78,7 @@ grub_normal_add_menu_entry (int argc, const char **args,
char **classes, const char *id,
const char *users, const char *hotkey,
const char *prefix, const char *sourcecode,
- int submenu)
+ int submenu, grub_blsuki_entry_t *blsuki)
{
int menu_hotkey = 0;
char **menu_args = NULL;
@@ -188,6 +188,7 @@ grub_normal_add_menu_entry (int argc, const char **args,
(*last)->args = menu_args;
(*last)->sourcecode = menu_sourcecode;
(*last)->submenu = submenu;
+ (*last)->blsuki = blsuki;
menu->size++;
return GRUB_ERR_NONE;
@@ -265,6 +266,9 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
if (! argc)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing arguments");
+ if (! grub_strlen (args[argc - 1]))
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "menuentry title is missing");
+
if (ctxt->state[3].set && ctxt->script)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "multiple menuentry definitions");
@@ -286,7 +290,8 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
users,
ctxt->state[2].arg, 0,
ctxt->state[3].arg,
- ctxt->extcmd->cmd->name[0] == 's');
+ ctxt->extcmd->cmd->name[0] == 's',
+ NULL);
src = args[argc - 1];
args[argc - 1] = NULL;
@@ -303,7 +308,7 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
ctxt->state[0].args, ctxt->state[4].arg,
users,
ctxt->state[2].arg, prefix, src + 1,
- ctxt->extcmd->cmd->name[0] == 's');
+ ctxt->extcmd->cmd->name[0] == 's', NULL);
src[len - 1] = ch;
args[argc - 1] = src;
diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c
index e61887862..a2549f9fd 100644
--- a/grub-core/commands/pgp.c
+++ b/grub-core/commands/pgp.c
@@ -136,10 +136,6 @@ struct signature_v4_header
grub_uint16_t hashed_sub;
} GRUB_PACKED;
-struct gcry_pk_spec *grub_crypto_pk_dsa;
-struct gcry_pk_spec *grub_crypto_pk_ecdsa;
-struct gcry_pk_spec *grub_crypto_pk_rsa;
-
struct
{
const char *name;
@@ -924,7 +920,7 @@ GRUB_MOD_INIT(pgp)
grub_memset (&pseudo_file, 0, sizeof (pseudo_file));
/* Not an ELF module, skip. */
- if (header->type != OBJ_TYPE_PUBKEY)
+ if (header->type != OBJ_TYPE_GPG_PUBKEY)
continue;
pseudo_file.fs = &pseudo_fs;
diff --git a/grub-core/commands/test.c b/grub-core/commands/test.c
index b585c3d70..ee47ab264 100644
--- a/grub-core/commands/test.c
+++ b/grub-core/commands/test.c
@@ -403,7 +403,7 @@ test_parse (char **args, int *argn, int argc, int *depth)
if (++(*depth) > MAX_TEST_RECURSION_DEPTH)
{
grub_error (GRUB_ERR_OUT_OF_RANGE, N_("max recursion depth exceeded"));
- depth--;
+ (*depth)--;
return ctx.or || ctx.and;
}
diff --git a/grub-core/commands/tpm2_key_protector/module.c b/grub-core/commands/tpm2_key_protector/module.c
index b84c2234f..1226b65e0 100644
--- a/grub-core/commands/tpm2_key_protector/module.c
+++ b/grub-core/commands/tpm2_key_protector/module.c
@@ -28,6 +28,7 @@
#include
#include
#include
+#include
#include "tpm2_args.h"
#include "tpm2.h"
@@ -47,6 +48,7 @@ typedef enum tpm2_protector_options
OPTION_MODE,
OPTION_PCRS,
OPTION_BANK,
+ OPTION_CAPPCRS,
OPTION_TPM2KEY,
OPTION_KEYFILE,
OPTION_SRK,
@@ -61,6 +63,8 @@ typedef struct tpm2_protector_context
grub_uint8_t pcr_count;
grub_srk_type_t srk_type;
TPM_ALG_ID_t bank;
+ grub_uint8_t cap_pcrs[TPM_MAX_PCRS];
+ grub_uint8_t cap_pcr_count;
const char *tpm2key;
const char *keyfile;
TPM_HANDLE_t srk;
@@ -100,6 +104,16 @@ static const struct grub_arg_option tpm2_protector_init_cmd_options[] =
N_("Bank of PCRs used to authorize key release: "
"SHA1, SHA256, SHA384 or SHA512. (default: SHA256)"),
},
+ {
+ .longarg = "cap-pcrs",
+ .shortarg = 'c',
+ .flags = 0,
+ .arg = NULL,
+ .type = ARG_TYPE_STRING,
+ .doc =
+ N_("Comma-separated list of PCRs to be capped after key release "
+ "e.g., '7,11'."),
+ },
/* SRK-mode options */
{
.longarg = "tpm2key",
@@ -1212,19 +1226,45 @@ tpm2_protector_nv_recover (const tpm2_protector_context_t *ctx,
return err;
}
+static grub_err_t
+tpm2_protector_cap_pcrs (const tpm2_protector_context_t *ctx)
+{
+ grub_uint8_t i;
+ grub_err_t err;
+
+ for (i = 0; i < ctx->cap_pcr_count; i++)
+ {
+ err = grub_tcg2_cap_pcr (ctx->cap_pcrs[i]);
+ if (err != GRUB_ERR_NONE)
+ return err;
+ }
+
+ return GRUB_ERR_NONE;
+}
+
static grub_err_t
tpm2_protector_recover (const tpm2_protector_context_t *ctx,
grub_uint8_t **key, grub_size_t *key_size)
{
+ grub_err_t err;
+
switch (ctx->mode)
{
case TPM2_PROTECTOR_MODE_SRK:
- return tpm2_protector_srk_recover (ctx, key, key_size);
+ err = tpm2_protector_srk_recover (ctx, key, key_size);
+ break;
case TPM2_PROTECTOR_MODE_NV:
- return tpm2_protector_nv_recover (ctx, key, key_size);
+ err = tpm2_protector_nv_recover (ctx, key, key_size);
+ break;
default:
- return GRUB_ERR_BAD_ARGUMENT;
+ err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Unknown Mode"));
}
+
+ /* Cap the selected PCRs when the key is unsealed successfully */
+ if (ctx->cap_pcr_count > 0 && err == GRUB_ERR_NONE)
+ err = tpm2_protector_cap_pcrs (ctx);
+
+ return err;
}
static grub_err_t
@@ -1364,6 +1404,15 @@ tpm2_protector_init_cmd_handler (grub_extcmd_context_t ctxt, int argc,
return err;
}
+ if (state[OPTION_CAPPCRS].set) /* cap-pcrs */
+ {
+ err = grub_tpm2_protector_parse_pcrs (state[OPTION_CAPPCRS].arg,
+ tpm2_protector_ctx.cap_pcrs,
+ &tpm2_protector_ctx.cap_pcr_count);
+ if (err != GRUB_ERR_NONE)
+ return err;
+ }
+
if (state[OPTION_TPM2KEY].set) /* tpm2key */
{
err = tpm2_protector_parse_file (state[OPTION_TPM2KEY].arg,
@@ -1465,6 +1514,7 @@ GRUB_MOD_INIT (tpm2_key_protector)
N_("[-m mode] "
"[-p pcr_list] "
"[-b pcr_bank] "
+ "[-c pcr_list] "
"[-T tpm2_key_file_path] "
"[-k sealed_key_file_path] "
"[-s srk_handle] "
diff --git a/grub-core/commands/usbtest.c b/grub-core/commands/usbtest.c
index 2c6d93fe6..3184ac9af 100644
--- a/grub-core/commands/usbtest.c
+++ b/grub-core/commands/usbtest.c
@@ -90,7 +90,7 @@ grub_usb_get_string (grub_usb_device_t dev, grub_uint8_t index, int langid,
0x06, (3 << 8) | index,
langid, descstr.length, (char *) descstrp);
- if (descstrp->length == 0)
+ if (descstrp->length < 2)
{
grub_free (descstrp);
*string = grub_strdup ("");
@@ -99,7 +99,7 @@ grub_usb_get_string (grub_usb_device_t dev, grub_uint8_t index, int langid,
return GRUB_USB_ERR_NONE;
}
- *string = grub_malloc (descstr.length * 2 + 1);
+ *string = grub_malloc (descstrp->length * 2 + 1);
if (! *string)
{
grub_free (descstrp);
diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index 9af665df3..290821bb6 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -29,6 +29,7 @@
#include
#include
#include
+#include
#ifdef GRUB_UTIL
#include
@@ -48,7 +49,8 @@ enum
OPTION_KEYFILE_OFFSET,
OPTION_KEYFILE_SIZE,
OPTION_HEADER,
- OPTION_PROTECTOR
+ OPTION_PROTECTOR,
+ OPTION_HWACCEL
};
static const struct grub_arg_option options[] =
@@ -64,6 +66,7 @@ static const struct grub_arg_option options[] =
{"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},
+ {"hw-accel", 'A', 0, N_("Enable hardware acceleration."), 0, 0},
{0, 0, 0, 0, 0, 0}
};
@@ -1420,7 +1423,7 @@ grub_cryptodisk_clear_key_cache (struct grub_cryptomount_args *cargs)
}
static grub_err_t
-grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args)
+__grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args)
{
struct grub_arg_list *state = ctxt->state;
struct grub_cryptomount_args cargs = {0};
@@ -1629,6 +1632,23 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args)
}
}
+static grub_err_t
+grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args)
+{
+ struct grub_arg_list *state = ctxt->state;
+ grub_err_t err;
+
+ if (state[OPTION_HWACCEL].set)
+ grub_enable_gcry_hwf ();
+
+ err = __grub_cmd_cryptomount (ctxt, argc, args);
+
+ if (state[OPTION_HWACCEL].set)
+ grub_reset_gcry_hwf ();
+
+ return err;
+}
+
static struct grub_disk_dev grub_cryptodisk_dev = {
.name = "cryptodisk",
.id = GRUB_DISK_DEVICE_CRYPTODISK_ID,
@@ -1898,7 +1918,7 @@ GRUB_MOD_INIT (cryptodisk)
cmd = grub_register_extcmd ("cryptomount", grub_cmd_cryptomount, 0,
N_("[ [-p password] | [-k keyfile"
" [-O keyoffset] [-S keysize] ] ] [-H file]"
- " [-P protector [-P protector ...]]"
+ " [-P protector [-P protector ...]] | [-A]"
" "),
N_("Mount a crypto device."), options);
grub_procfs_register ("luks_script", &luks_script);
diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c
index 722910d64..bf22ebb63 100644
--- a/grub-core/disk/geli.c
+++ b/grub-core/disk/geli.c
@@ -464,9 +464,7 @@ geli_recover_key (grub_disk_t source, grub_cryptodisk_t dev, grub_cryptomount_ar
grub_crypto_hmac_write (hnd, header.salt, sizeof (header.salt));
grub_crypto_hmac_write (hnd, cargs->key_data, cargs->key_len);
- gcry_err = grub_crypto_hmac_fini (hnd, geomkey);
- if (gcry_err)
- return grub_crypto_gcry_error (gcry_err);
+ grub_crypto_hmac_fini (hnd, geomkey);
}
gcry_err = grub_crypto_hmac_buffer (dev->hash, geomkey,
diff --git a/grub-core/disk/ieee1275/ofdisk.c b/grub-core/disk/ieee1275/ofdisk.c
index dbc0f1aba..f4624a4ad 100644
--- a/grub-core/disk/ieee1275/ofdisk.c
+++ b/grub-core/disk/ieee1275/ofdisk.c
@@ -93,6 +93,7 @@ ofdisk_hash_add_real (char *devpath)
grub_add (sz, sizeof ("ieee1275/"), &sz))
{
grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of device path"));
+ grub_free (p);
return NULL;
}
@@ -109,6 +110,8 @@ ofdisk_hash_add_real (char *devpath)
if (grub_add (grub_strlen (p->devpath), 3, &sz))
{
grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of an open path"));
+ grub_free (p->grub_devpath);
+ grub_free (p);
return NULL;
}
diff --git a/grub-core/disk/luks2.c b/grub-core/disk/luks2.c
index b17cd2115..a3608e233 100644
--- a/grub-core/disk/luks2.c
+++ b/grub-core/disk/luks2.c
@@ -39,6 +39,7 @@ GRUB_MOD_LICENSE ("GPLv3+");
enum grub_luks2_kdf_type
{
LUKS2_KDF_TYPE_ARGON2I,
+ LUKS2_KDF_TYPE_ARGON2ID,
LUKS2_KDF_TYPE_PBKDF2
};
typedef enum grub_luks2_kdf_type grub_luks2_kdf_type_t;
@@ -159,13 +160,21 @@ luks2_parse_keyslot (grub_luks2_keyslot_t *out, const grub_json_t *keyslot)
grub_json_getstring (&type, &kdf, "type") ||
grub_json_getstring (&out->kdf.salt, &kdf, "salt"))
return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing or invalid KDF");
- else if (!grub_strcmp (type, "argon2i") || !grub_strcmp (type, "argon2id"))
+ else if (!grub_strcmp (type, "argon2i"))
{
out->kdf.type = LUKS2_KDF_TYPE_ARGON2I;
if (grub_json_getint64 (&out->kdf.u.argon2i.time, &kdf, "time") ||
grub_json_getint64 (&out->kdf.u.argon2i.memory, &kdf, "memory") ||
grub_json_getint64 (&out->kdf.u.argon2i.cpus, &kdf, "cpus"))
- return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing Argon2i parameters");
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing Argon2i parameters");
+ }
+ else if (!grub_strcmp (type, "argon2id"))
+ {
+ out->kdf.type = LUKS2_KDF_TYPE_ARGON2ID;
+ if (grub_json_getint64 (&out->kdf.u.argon2i.time, &kdf, "time") ||
+ grub_json_getint64 (&out->kdf.u.argon2i.memory, &kdf, "memory") ||
+ grub_json_getint64 (&out->kdf.u.argon2i.cpus, &kdf, "cpus"))
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing Argon2id parameters");
}
else if (!grub_strcmp (type, "pbkdf2"))
{
@@ -444,6 +453,8 @@ luks2_decrypt_key (grub_uint8_t *out_key,
grub_uint8_t salt[GRUB_CRYPTODISK_MAX_KEYLEN];
grub_uint8_t *split_key = NULL;
idx_t saltlen = sizeof (salt);
+ int subalgo;
+ unsigned long param[4];
char cipher[32], *p;
const gcry_md_spec_t *hash;
gcry_err_code_t gcry_ret;
@@ -460,8 +471,29 @@ luks2_decrypt_key (grub_uint8_t *out_key,
switch (k->kdf.type)
{
case LUKS2_KDF_TYPE_ARGON2I:
- ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Argon2 not supported");
- goto err;
+ case LUKS2_KDF_TYPE_ARGON2ID:
+ if (k->kdf.type == LUKS2_KDF_TYPE_ARGON2I)
+ subalgo = GRUB_GCRY_KDF_ARGON2I;
+ else
+ subalgo = GRUB_GCRY_KDF_ARGON2ID;
+
+ param[0] = k->area.key_size;
+ param[1] = k->kdf.u.argon2i.time;
+ param[2] = k->kdf.u.argon2i.memory;
+ param[3] = k->kdf.u.argon2i.cpus;
+
+ gcry_ret = grub_crypto_argon2 (subalgo, param, 4,
+ passphrase, passphraselen,
+ salt, saltlen,
+ NULL, 0, NULL, 0,
+ k->area.key_size, area_key);
+ if (gcry_ret)
+ {
+ ret = grub_crypto_gcry_error (gcry_ret);
+ goto err;
+ }
+
+ break;
case LUKS2_KDF_TYPE_PBKDF2:
hash = grub_crypto_lookup_md_by_name (k->kdf.u.pbkdf2.hash);
if (!hash)
diff --git a/grub-core/efiemu/loadcore.c b/grub-core/efiemu/loadcore.c
index 2b924623f..4a091bee0 100644
--- a/grub-core/efiemu/loadcore.c
+++ b/grub-core/efiemu/loadcore.c
@@ -203,6 +203,9 @@ grub_efiemu_count_symbols (const Elf_Ehdr *e)
grub_efiemu_elfsyms = (struct grub_efiemu_elf_sym *)
grub_calloc (grub_efiemu_nelfsyms, sizeof (struct grub_efiemu_elf_sym));
+ if (grub_efiemu_elfsyms == NULL)
+ return grub_errno;
+
/* Relocators */
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
i < e->e_shnum;
diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 7bf8d922f..61dd1676a 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -2334,14 +2334,20 @@ struct embed_region {
};
/*
- * https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs(5)#BOOTLOADER_SUPPORT
+ * https://btrfs.readthedocs.io/en/latest/btrfs-man5.html#man-btrfs5-bootloader-support
+ * or invoke "man 5 btrfs" and visit the "BOOTLOADER SUPPORT" subsection.
* The first 1 MiB on each device is unused with the exception of primary
* superblock that is on the offset 64 KiB and spans 4 KiB.
+ *
+ * Note: If this table is modified, also update
+ * util/grub-editenv.c::fs_envblk_spec, which describes the file-system
+ * specific layout of reserved raw blocks used as environment blocks so that
+ * both stay consistent.
*/
static const struct {
struct embed_region available;
- struct embed_region used[6];
+ struct embed_region used[9];
} btrfs_head = {
.available = {0, GRUB_DISK_KiB_TO_SECTORS (1024)}, /* The first 1 MiB. */
.used = {
@@ -2349,6 +2355,9 @@ static const struct {
{GRUB_DISK_KiB_TO_SECTORS (64) - 1, 1}, /* Overflow guard. */
{GRUB_DISK_KiB_TO_SECTORS (64), GRUB_DISK_KiB_TO_SECTORS (4)}, /* 4 KiB superblock. */
{GRUB_DISK_KiB_TO_SECTORS (68), 1}, /* Overflow guard. */
+ {(GRUB_ENV_BTRFS_OFFSET >> GRUB_DISK_SECTOR_BITS) - 1, 1}, /* Overflow guard. */
+ {(GRUB_ENV_BTRFS_OFFSET >> GRUB_DISK_SECTOR_BITS), 1}, /* Environment Block. */
+ {(GRUB_ENV_BTRFS_OFFSET >> GRUB_DISK_SECTOR_BITS) + 1, 1}, /* Overflow guard. */
{GRUB_DISK_KiB_TO_SECTORS (1024) - 1, 1}, /* Overflow guard. */
{0, 0} /* Array terminator. */
}
diff --git a/grub-core/fs/hfsplus.c b/grub-core/fs/hfsplus.c
index 3f203abcc..12d3f2641 100644
--- a/grub-core/fs/hfsplus.c
+++ b/grub-core/fs/hfsplus.c
@@ -736,7 +736,9 @@ list_nodes (void *record, void *hook_arg)
int mode = (grub_be_to_cpu16 (fileinfo->mode)
& GRUB_HFSPLUS_FILEMODE_MASK);
- if (mode == GRUB_HFSPLUS_FILEMODE_REG)
+ if (mode == 0) /* Created by pre-Mac OS X. */
+ type = GRUB_FSHELP_REG;
+ else if (mode == GRUB_HFSPLUS_FILEMODE_REG)
type = GRUB_FSHELP_REG;
else if (mode == GRUB_HFSPLUS_FILEMODE_SYMLINK)
type = GRUB_FSHELP_SYMLINK;
diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c
index 5b0a18f3d..bb3cec4e6 100644
--- a/grub-core/fs/ntfs.c
+++ b/grub-core/fs/ntfs.c
@@ -233,7 +233,12 @@ next_attribute (grub_uint8_t *curr_attribute, void *end, bool validate)
return NULL;
next += u16at (curr_attribute, 4);
- if (validate && validate_attribute (next, end) == false)
+ if (validate)
+ {
+ if (validate_attribute (next, end) == false)
+ return NULL;
+ }
+ else if (next >= (grub_uint8_t *) end)
return NULL;
return next;
diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c
index afe821f9b..83dfa6d52 100644
--- a/grub-core/fs/zfs/zfs.c
+++ b/grub-core/fs/zfs/zfs.c
@@ -2743,7 +2743,7 @@ dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type,
grub_uint64_t blkid, blksz; /* the block id this object dnode is in */
int epbs; /* shift of number of dnodes in a block */
int idx; /* index within a block */
- void *dnbuf;
+ dnode_phys_t *dnbuf;
grub_err_t err;
grub_zfs_endian_t endian;
@@ -2773,7 +2773,7 @@ dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type,
grub_dprintf ("zfs", "endian = %d, blkid=%llx\n", mdn->endian,
(unsigned long long) blkid);
- err = dmu_read (mdn, blkid, &dnbuf, &endian, data);
+ err = dmu_read (mdn, blkid, (void **) &dnbuf, &endian, data);
if (err)
return err;
grub_dprintf ("zfs", "alive\n");
@@ -2795,7 +2795,7 @@ dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type,
data->dnode_endian = endian;
}
- grub_memmove (&(buf->dn), (dnode_phys_t *) dnbuf + idx, DNODE_SIZE);
+ grub_memmove (&(buf->dn), dnbuf + idx, DNODE_SIZE);
if (data->dnode_buf == 0)
/* dnbuf not used anymore if data->dnode_mdn malloc failed */
grub_free (dnbuf);
diff --git a/grub-core/gettext/gettext.c b/grub-core/gettext/gettext.c
index 9ffc73428..edebed998 100644
--- a/grub-core/gettext/gettext.c
+++ b/grub-core/gettext/gettext.c
@@ -502,6 +502,8 @@ grub_cmd_translate (grub_command_t cmd __attribute__ ((unused)),
return 0;
}
+static grub_command_t cmd;
+
GRUB_MOD_INIT (gettext)
{
const char *lang;
@@ -521,13 +523,14 @@ GRUB_MOD_INIT (gettext)
grub_register_variable_hook ("locale_dir", NULL, read_main);
grub_register_variable_hook ("secondary_locale_dir", NULL, read_secondary);
- grub_register_command_p1 ("gettext", grub_cmd_translate,
- N_("STRING"),
- /* TRANSLATORS: It refers to passing the string through gettext.
- So it's "translate" in the same meaning as in what you're
- doing now.
- */
- N_("Translates the string with the current settings."));
+ cmd = grub_register_command_p1 ("gettext", grub_cmd_translate,
+ N_("STRING"),
+ /*
+ * TRANSLATORS: It refers to passing the string through gettext.
+ * So it's "translate" in the same meaning as in what you're
+ * doing now.
+ */
+ N_("Translates the string with the current settings."));
/* Reload .mo file information if lang changes. */
grub_register_variable_hook ("lang", NULL, grub_gettext_env_write_lang);
@@ -544,6 +547,8 @@ GRUB_MOD_FINI (gettext)
grub_register_variable_hook ("secondary_locale_dir", NULL, NULL);
grub_register_variable_hook ("lang", NULL, NULL);
+ grub_unregister_command (cmd);
+
grub_gettext_delete_list (&main_context);
grub_gettext_delete_list (&secondary_context);
diff --git a/grub-core/io/zstdio.c b/grub-core/io/zstdio.c
new file mode 100644
index 000000000..379eba0c6
--- /dev/null
+++ b/grub-core/io/zstdio.c
@@ -0,0 +1,251 @@
+/* zstdio.c - decompression support for zstd */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2025 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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#include "zstd.h"
+
+#define STREAM_HEADER_SIZE 16
+
+struct zstdio
+{
+ grub_file_t file;
+ ZSTD_DCtx *dctx;
+ grub_size_t insize;
+ grub_size_t outsize;
+
+ ZSTD_outBuffer output;
+ ZSTD_inBuffer input;
+
+ grub_off_t saved_offset;
+ grub_uint8_t bufs[];
+};
+typedef struct zstdio *zstdio_t;
+
+static struct grub_fs zstdio_fs;
+
+static bool
+test_header (grub_file_t file)
+{
+ zstdio_t zstdio = file->data;
+ size_t zret;
+
+ zstdio->input.pos = 0;
+ zstdio->output.pos = 0;
+ zstdio->output.size = zstdio->outsize;
+ zstdio->input.size = grub_file_read (zstdio->file, zstdio->bufs,
+ STREAM_HEADER_SIZE);
+ if (zstdio->input.size != STREAM_HEADER_SIZE)
+ return false;
+
+ zret = ZSTD_decompressStream (zstdio->dctx, &zstdio->output, &zstdio->input);
+ if (ZSTD_isError (zret))
+ return false;
+
+ return true;
+}
+
+static grub_file_t
+grub_zstdio_open (grub_file_t io, enum grub_file_type type)
+{
+ grub_file_t file;
+ zstdio_t zstdio;
+
+ if (type & GRUB_FILE_TYPE_NO_DECOMPRESS)
+ return io;
+
+ file = (grub_file_t) grub_zalloc (sizeof (*file));
+ if (file == NULL)
+ return NULL;
+
+ zstdio = grub_zalloc (sizeof (zstdio_t) + ZSTD_DStreamInSize () +
+ ZSTD_DStreamOutSize ());
+ if (zstdio == NULL)
+ {
+ grub_free (file);
+ return NULL;
+ }
+
+ zstdio->file = io;
+ zstdio->insize = ZSTD_DStreamInSize ();
+ zstdio->outsize = ZSTD_DStreamOutSize ();
+ zstdio->input.src = zstdio->bufs;
+ zstdio->output.dst = &zstdio->bufs[zstdio->insize];
+
+ file->device = io->device;
+ file->data = zstdio;
+ file->fs = &zstdio_fs;
+ file->size = GRUB_FILE_SIZE_UNKNOWN;
+ file->not_easily_seekable = 1;
+
+ if (grub_file_tell (zstdio->file) != 0)
+ if (grub_file_seek (zstdio->file, 0) == (grub_off_t) -1)
+ {
+ grub_free (file);
+ grub_free (zstdio);
+ return NULL;
+ }
+
+ zstdio->dctx = ZSTD_createDCtx ();
+ if (zstdio->dctx == NULL)
+ {
+ grub_free (file);
+ grub_free (zstdio);
+ return NULL;
+ }
+
+ if (test_header (file) == false)
+ {
+ grub_errno = GRUB_ERR_NONE;
+ if (grub_file_seek (io, 0) == (grub_off_t) -1)
+ {
+ grub_free (file);
+ grub_free (zstdio);
+ return NULL;
+ }
+
+ ZSTD_freeDCtx (zstdio->dctx);
+ grub_free (zstdio);
+ grub_free (file);
+
+ return io;
+ }
+
+ return file;
+}
+
+static grub_ssize_t
+grub_zstdio_read (grub_file_t file, char *buf, grub_size_t len)
+{
+ zstdio_t zstdio = file->data;
+ grub_ssize_t ret = 0;
+ grub_ssize_t readret;
+ grub_off_t current_offset;
+ grub_off_t new_offset;
+ grub_size_t delta;
+ grub_size_t zret;
+
+ /* If seek backward need to reset decoder and start from beginning of file. */
+ if (file->offset < zstdio->saved_offset)
+ {
+ ZSTD_initDStream (zstdio->dctx);
+ zstdio->input.pos = 0;
+ zstdio->input.size = 0;
+ zstdio->output.pos = 0;
+ zstdio->saved_offset = 0;
+ grub_file_seek (zstdio->file, 0);
+ }
+
+ current_offset = zstdio->saved_offset;
+
+ while (len > 0)
+ {
+ zstdio->output.size = file->offset + ret + len - current_offset;
+ if (zstdio->output.size > zstdio->outsize)
+ zstdio->output.size = zstdio->outsize;
+ if (zstdio->input.pos == zstdio->input.size)
+ {
+ readret = grub_file_read (zstdio->file, zstdio->bufs,
+ zstdio->insize);
+ if (readret < 0)
+ return -1;
+
+ zstdio->input.size = readret;
+ zstdio->input.pos = 0;
+ }
+
+ zret = ZSTD_decompressStream (zstdio->dctx, &zstdio->output,
+ &zstdio->input);
+ if (ZSTD_isError (zret))
+ {
+ grub_error (GRUB_ERR_BAD_COMPRESSED_DATA,
+ N_("zstd file corrupted or unsupported block options"));
+ return -1;
+ }
+
+ new_offset = current_offset + zstdio->output.pos;
+
+ /* Store first chunk of data in buffer. */
+ if (file->offset <= new_offset)
+ {
+ delta = new_offset - (file->offset + ret);
+ grub_memmove (buf, (grub_uint8_t *) zstdio->output.dst +
+ (zstdio->output.pos - delta),
+ delta);
+ len -= delta;
+ buf += delta;
+ ret += delta;
+ }
+ current_offset = new_offset;
+
+ zstdio->output.pos = 0;
+
+ if (zstdio->input.pos == 0 && zstdio->output.pos == 0)
+ break;
+ }
+
+ if (ret >= 0)
+ zstdio->saved_offset = file->offset + ret;
+
+ return ret;
+}
+
+/* Release everything, including the underlying file object. */
+static grub_err_t
+grub_zstdio_close (grub_file_t file)
+{
+ zstdio_t zstdio = file->data;
+
+ ZSTD_freeDCtx (zstdio->dctx);
+
+ grub_file_close (zstdio->file);
+ grub_free (zstdio);
+
+ /* Device must not be closed twice. */
+ file->device = 0;
+ file->name = 0;
+ return grub_errno;
+}
+
+static struct grub_fs zstdio_fs = {
+ .name = "zstdio",
+ .fs_dir = 0,
+ .fs_open = 0,
+ .fs_read = grub_zstdio_read,
+ .fs_close = grub_zstdio_close,
+ .fs_label = 0,
+ .next = 0
+};
+
+GRUB_MOD_INIT (zstdio)
+{
+ grub_file_filter_register (GRUB_FILE_FILTER_ZSTDIO, grub_zstdio_open);
+}
+
+GRUB_MOD_FINI (zstdio)
+{
+ grub_file_filter_unregister (GRUB_FILE_FILTER_ZSTDIO);
+}
diff --git a/grub-core/kern/command.c b/grub-core/kern/command.c
index 5812e131c..a1b4c81a9 100644
--- a/grub-core/kern/command.c
+++ b/grub-core/kern/command.c
@@ -104,6 +104,9 @@ grub_register_command_lockdown (const char *name,
void
grub_unregister_command (grub_command_t cmd)
{
+ if (cmd == NULL)
+ return;
+
if ((cmd->prio & GRUB_COMMAND_FLAG_ACTIVE) && (cmd->next))
cmd->next->prio |= GRUB_COMMAND_FLAG_ACTIVE;
grub_list_remove (GRUB_AS_LIST (cmd));
diff --git a/grub-core/kern/compiler-rt.c b/grub-core/kern/compiler-rt.c
index eda689a0c..8f3865e95 100644
--- a/grub-core/kern/compiler-rt.c
+++ b/grub-core/kern/compiler-rt.c
@@ -24,7 +24,7 @@
void * GRUB_BUILTIN_ATTR
memcpy (void *dest, const void *src, grub_size_t n)
{
- return grub_memmove (dest, src, n);
+ return grub_memcpy (dest, src, n);
}
void * GRUB_BUILTIN_ATTR
memmove (void *dest, const void *src, grub_size_t n)
@@ -372,11 +372,11 @@ grub_int32_t
__aeabi_idiv (grub_int32_t a, grub_int32_t b)
__attribute__ ((alias ("__divsi3")));
void *__aeabi_memcpy (void *dest, const void *src, grub_size_t n)
- __attribute__ ((alias ("grub_memcpy")));
+ __attribute__ ((alias ("memcpy")));
void *__aeabi_memcpy4 (void *dest, const void *src, grub_size_t n)
- __attribute__ ((alias ("grub_memcpy")));
+ __attribute__ ((alias ("memcpy")));
void *__aeabi_memcpy8 (void *dest, const void *src, grub_size_t n)
- __attribute__ ((alias ("grub_memcpy")));
+ __attribute__ ((alias ("memcpy")));
void *__aeabi_memset (void *s, int c, grub_size_t n)
__attribute__ ((alias ("memset")));
diff --git a/grub-core/kern/efi/sb.c b/grub-core/kern/efi/sb.c
index 4409e03c5..b2bf38a54 100644
--- a/grub-core/kern/efi/sb.c
+++ b/grub-core/kern/efi/sb.c
@@ -152,6 +152,8 @@ shim_lock_verifier_init (grub_file_t io __attribute__ ((unused)),
case GRUB_FILE_TYPE_TESTLOAD:
case GRUB_FILE_TYPE_GET_SIZE:
case GRUB_FILE_TYPE_ZFS_ENCRYPTION_KEY:
+ case GRUB_FILE_TYPE_CRYPTODISK_ENCRYPTION_KEY:
+ case GRUB_FILE_TYPE_CRYPTODISK_DETACHED_HEADER:
case GRUB_FILE_TYPE_CAT:
case GRUB_FILE_TYPE_HEXCAT:
case GRUB_FILE_TYPE_CMP:
diff --git a/grub-core/kern/err.c b/grub-core/kern/err.c
index 53c734de7..ba04b57fb 100644
--- a/grub-core/kern/err.c
+++ b/grub-core/kern/err.c
@@ -33,15 +33,24 @@ static struct grub_error_saved grub_error_stack_items[GRUB_ERROR_STACK_SIZE];
static int grub_error_stack_pos;
static int grub_error_stack_assert;
+#ifdef grub_error
+#undef grub_error
+#endif
+
grub_err_t
-grub_error (grub_err_t n, const char *fmt, ...)
+grub_error (grub_err_t n, const char *file, const char *function, const int line, const char *fmt, ...)
{
va_list ap;
+ int m;
grub_errno = n;
+ m = grub_snprintf (grub_errmsg, sizeof (grub_errmsg), "%s:%s:%d:", file, function, line);
+ if (m < 0)
+ m = 0;
+
va_start (ap, fmt);
- grub_vsnprintf (grub_errmsg, sizeof (grub_errmsg), _(fmt), ap);
+ grub_vsnprintf (grub_errmsg + m, sizeof (grub_errmsg) - m, _(fmt), ap);
va_end (ap);
return n;
diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c
index 6e7efe89a..eb52fd25f 100644
--- a/grub-core/kern/file.c
+++ b/grub-core/kern/file.c
@@ -201,12 +201,12 @@ grub_file_read (grub_file_t file, void *buf, grub_size_t len)
grub_err_t
grub_file_close (grub_file_t file)
{
- if (file->fs->mod)
- grub_dl_unref (file->fs->mod);
-
if (file->fs->fs_close)
(file->fs->fs_close) (file);
+ if (file->fs->mod)
+ grub_dl_unref (file->fs->mod);
+
if (file->device)
grub_device_close (file->device);
grub_free (file->name);
diff --git a/grub-core/kern/i386/xen/pvh.c b/grub-core/kern/i386/xen/pvh.c
index 91fbca859..293b615e8 100644
--- a/grub-core/kern/i386/xen/pvh.c
+++ b/grub-core/kern/i386/xen/pvh.c
@@ -352,6 +352,10 @@ grub_xen_setup_pvh (void)
grub_xen_mm_init_regions ();
grub_rsdp_addr = pvh_start_info->rsdp_paddr;
+
+ grub_strncpy ((char *) grub_xen_start_page_addr->cmd_line,
+ (const char *)(grub_addr_t) pvh_start_info->cmdline_paddr,
+ GRUB_XEN_MAX_GUEST_CMDLINE);
}
grub_err_t
diff --git a/grub-core/kern/ieee1275/ieee1275.c b/grub-core/kern/ieee1275/ieee1275.c
index 36ca2dbfc..afa37a9f0 100644
--- a/grub-core/kern/ieee1275/ieee1275.c
+++ b/grub-core/kern/ieee1275/ieee1275.c
@@ -23,7 +23,6 @@
#define IEEE1275_PHANDLE_INVALID ((grub_ieee1275_cell_t) -1)
#define IEEE1275_IHANDLE_INVALID ((grub_ieee1275_cell_t) 0)
-#define IEEE1275_CELL_INVALID ((grub_ieee1275_cell_t) -1)
diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
index a5586f85b..7cdcd750f 100644
--- a/grub-core/kern/ieee1275/init.c
+++ b/grub-core/kern/ieee1275/init.c
@@ -49,6 +49,16 @@
#if defined(__powerpc__) || defined(__i386__)
#include
#endif
+#if defined(__powerpc__)
+#include
+#include
+#include
+#endif
+
+#ifdef __powerpc__
+#define GRUB_SB_DISABLED ((grub_uint32_t) 0)
+#define GRUB_SB_ENFORCE ((grub_uint32_t) 2)
+#endif
/* The maximum heap size we're going to claim at boot. Not used by sparc. */
#ifdef __i386__
@@ -152,6 +162,8 @@ grub_machine_get_bootlocation (char **device, char **path)
char *bootpath;
char *filename;
char *type;
+ char *ret_device = NULL;
+ char *ret_path = NULL;
bootpath = grub_ieee1275_get_boot_dev ();
if (! bootpath)
@@ -167,7 +179,7 @@ grub_machine_get_bootlocation (char **device, char **path)
dev = grub_ieee1275_get_aliasdevname (bootpath);
canon = grub_ieee1275_canonicalise_devname (dev);
if (! canon)
- return;
+ goto done;
ptr = canon + grub_strlen (canon) - 1;
while (ptr > canon && (*ptr == ',' || *ptr == ':'))
ptr--;
@@ -175,13 +187,17 @@ grub_machine_get_bootlocation (char **device, char **path)
*ptr = 0;
if (grub_ieee1275_net_config)
- grub_ieee1275_net_config (canon, device, path, bootpath);
+ grub_ieee1275_net_config (canon, &ret_device, &ret_path, bootpath);
grub_free (dev);
grub_free (canon);
+
+ /* Use path from net config if it is provided by cached DHCP info */
+ if (ret_path != NULL)
+ goto done;
+ /* Fall through to use firmware bootpath */
}
else
- *device = grub_ieee1275_encode_devname (bootpath);
- grub_free (type);
+ ret_device = grub_ieee1275_encode_devname (bootpath);
filename = grub_ieee1275_get_filename (bootpath);
if (filename)
@@ -194,10 +210,18 @@ grub_machine_get_bootlocation (char **device, char **path)
*lastslash = '\0';
grub_translate_ieee1275_path (filename);
- *path = filename;
+ ret_path = filename;
}
}
+
+ done:
+ grub_free (type);
grub_free (bootpath);
+
+ if (device != NULL)
+ *device = ret_device;
+ if (path != NULL)
+ *path = ret_path;
}
/* Claim some available memory in the first /memory node. */
@@ -994,7 +1018,51 @@ grub_parse_cmdline (void)
}
}
}
+#ifdef __powerpc__
+static void
+grub_ieee1275_get_secure_boot (void)
+{
+ grub_ieee1275_phandle_t root;
+ grub_uint32_t sb_mode = GRUB_SB_DISABLED;
+ grub_int32_t rc;
+ rc = grub_ieee1275_finddevice ("/", &root);
+ if (rc != 0)
+ {
+ grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't find / node");
+ return;
+ }
+
+ rc = grub_ieee1275_get_integer_property (root, "ibm,secure-boot", &sb_mode, sizeof (sb_mode), 0);
+ if (rc != 0)
+ {
+ grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't examine /ibm,secure-boot property");
+ return;
+ }
+ /*
+ * Secure Boot Mode:
+ * 0 - disabled
+ * No signature verification is performed. This is the default.
+ * 1 - audit
+ * Signature verification is performed and if signature verification
+ * fails, display the errors and allow the boot to continue.
+ * 2 - enforce
+ * Lockdown the GRUB. Signature verification is performed and If
+ * signature verification fails, display the errors and stop the boot.
+ *
+ * Now, only support disabled and enforce.
+ */
+ if (sb_mode == GRUB_SB_ENFORCE)
+ {
+ grub_dprintf ("ieee1275", "Secure Boot Enabled\n");
+ grub_lockdown ();
+ }
+ else
+ grub_dprintf ("ieee1275", "Secure Boot Disabled\n");
+
+ grub_pks_keystore_init ();
+}
+#endif /* __powerpc__ */
grub_addr_t grub_modbase;
void
@@ -1020,6 +1088,10 @@ grub_machine_init (void)
#else
grub_install_get_time_ms (grub_rtc_get_time_ms);
#endif
+
+#ifdef __powerpc__
+ grub_ieee1275_get_secure_boot ();
+#endif
}
void
diff --git a/grub-core/kern/ieee1275/openfw.c b/grub-core/kern/ieee1275/openfw.c
index 11b2beb2f..06757f5d8 100644
--- a/grub-core/kern/ieee1275/openfw.c
+++ b/grub-core/kern/ieee1275/openfw.c
@@ -201,6 +201,11 @@ grub_ieee1275_devalias_next (struct grub_ieee1275_devalias *alias)
alias->path = 0;
}
tmp = grub_strdup (alias->name);
+ if (tmp == NULL)
+ {
+ grub_ieee1275_devalias_free (alias);
+ return 0;
+ }
if (grub_ieee1275_next_property (alias->parent_dev, tmp,
alias->name) <= 0)
{
@@ -432,9 +437,15 @@ grub_ieee1275_parse_args (const char *path, enum grub_ieee1275_parse_type ptype)
ret = grub_strdup (args);
else
ret = grub_strndup (args, (grub_size_t)(comma - args));
- /* Consistently provide numbered partitions to GRUB.
- OpenBOOT traditionally uses alphabetical partition
- specifiers. */
+
+ if (ret == NULL)
+ return 0;
+
+ /*
+ * Consistently provide numbered partitions to GRUB.
+ * OpenBOOT traditionally uses alphabetical partition
+ * specifiers.
+ */
if (ret[0] >= 'a' && ret[0] <= 'z')
ret[0] = '1' + (ret[0] - 'a');
grub_free (args);
@@ -501,7 +512,20 @@ grub_ieee1275_encode_devname (const char *path)
}
if (partition && partition[0])
{
- unsigned int partno = grub_strtoul (partition, 0, 0);
+ unsigned long partno;
+ const char *endptr;
+
+ partno = grub_strtoul (partition, &endptr, 0);
+ grub_errno = GRUB_ERR_NONE;
+ if (*endptr != '\0' || partno > 65535 ||
+ (partno == 0 && ! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_0_BASED_PARTITIONS)))
+ {
+ grub_free (partition);
+ grub_free (device);
+ grub_free (encoding);
+ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid partition number"));
+ return NULL;
+ }
*optr++ = ',';
@@ -509,7 +533,7 @@ grub_ieee1275_encode_devname (const char *path)
/* GRUB partition 1 is OF partition 0. */
partno++;
- grub_snprintf (optr, sizeof ("XXXXXXXXXXXX"), "%d", partno);
+ grub_snprintf (optr, sizeof ("XXXXXXXXXXXX"), "%lu", partno);
}
else
*optr = '\0';
diff --git a/grub-core/kern/mips/arc/init.c b/grub-core/kern/mips/arc/init.c
index 2ed3ff319..2b76988c7 100644
--- a/grub-core/kern/mips/arc/init.c
+++ b/grub-core/kern/mips/arc/init.c
@@ -403,6 +403,9 @@ grub_machine_get_bootlocation (char **device, char **path)
if (!syspart)
return;
loaddev = grub_strdup (syspart);
+ if (loaddev == NULL)
+ return;
+
}
partptr = get_part (loaddev);
diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
index 2b7922393..18c34b8eb 100644
--- a/grub-core/kern/misc.c
+++ b/grub-core/kern/misc.c
@@ -26,6 +26,7 @@
#include
#include
#include
+#include
union printf_arg
{
@@ -99,6 +100,37 @@ grub_memmove (void *dest, const void *src, grub_size_t n)
return dest;
}
+static void *
+__memcpy_aligned (void *dest, const void *src, grub_size_t n)
+{
+ grub_addr_t *dw = (grub_addr_t *) dest;
+ const grub_addr_t *sw = (const grub_addr_t *) src;
+ grub_uint8_t *d;
+ const grub_uint8_t *s;
+
+ for (; n >= sizeof (grub_addr_t); n -= sizeof (grub_addr_t))
+ *dw++ = *sw++;
+
+ d = (grub_uint8_t *) dw;
+ s = (const grub_uint8_t *) sw;
+ for (; n > 0; n--)
+ *d++ = *s++;
+
+ return dest;
+}
+
+void *
+grub_memcpy (void *dest, const void *src, grub_size_t n)
+{
+ /* Check if dest and src are aligned and n >= sizeof(grub_addr_t). */
+ if (((grub_addr_t) dest & (sizeof (grub_addr_t) - 1)) == 0 &&
+ ((grub_addr_t) src & (sizeof (grub_addr_t) - 1)) == 0 &&
+ n >= sizeof (grub_addr_t))
+ return __memcpy_aligned (dest, src, n);
+
+ return grub_memmove (dest, src, n);
+}
+
char *
grub_strcpy (char *dest, const char *src)
{
@@ -231,14 +263,14 @@ grub_debug_enabled (const char * condition)
}
void
-grub_real_dprintf (const char *file, const int line, const char *condition,
+grub_real_dprintf (const char *file, const char *function, const int line, const char *condition,
const char *fmt, ...)
{
va_list args;
if (grub_debug_enabled (condition))
{
- grub_printf ("%s:%d:%s: ", file, line, condition);
+ grub_printf ("%s:%s:%d:%s: ", file, function, line, condition);
va_start (args, fmt);
grub_vprintf (fmt, args);
va_end (args);
@@ -401,6 +433,68 @@ grub_strword (const char *haystack, const char *needle)
return 0;
}
+char *
+grub_strtok_r (char *s, const char *delim, char **save_ptr)
+{
+ char *token;
+ const char *c;
+ bool is_delim;
+
+ if (s == NULL)
+ s = *save_ptr;
+
+ /* Scan leading delimiters. */
+ while (*s != '\0')
+ {
+ is_delim = false;
+ for (c = delim; *c != '\0'; c++)
+ {
+ if (*s == *c)
+ {
+ is_delim = true;
+ break;
+ }
+ }
+ if (is_delim == true)
+ s++;
+ else
+ break;
+ }
+
+ if (*s == '\0')
+ {
+ *save_ptr = s;
+ return NULL;
+ }
+
+ /* Find the end of the token. */
+ token = s;
+ while (*s != '\0')
+ {
+ for (c = delim; *c != '\0'; c++)
+ {
+ if (*s == *c)
+ {
+ *s = '\0';
+ *save_ptr = s + 1;
+ return token;
+ }
+ }
+ s++;
+ }
+
+ *save_ptr = s;
+ return token;
+}
+
+char *
+grub_strtok (char *s, const char *delim)
+{
+ static char *last;
+
+ return grub_strtok_r (s, delim, &last);
+}
+
int
grub_isspace (int c)
{
@@ -739,6 +833,9 @@ parse_printf_arg_fmt (const char *fmt0, struct printf_args *args,
COMPILE_TIME_ASSERT (sizeof (long) <= sizeof (long long));
COMPILE_TIME_ASSERT (sizeof (long long) == sizeof (void *)
|| sizeof (int) == sizeof (void *));
+ COMPILE_TIME_ASSERT (sizeof (size_t) == sizeof (unsigned)
+ || sizeof (size_t) == sizeof (unsigned long)
+ || sizeof (size_t) == sizeof (unsigned long long));
fmt = fmt0;
while ((c = *fmt++) != 0)
@@ -773,11 +870,17 @@ parse_printf_arg_fmt (const char *fmt0, struct printf_args *args,
fmt++;
c = *fmt++;
+ if (c == 'z')
+ {
+ c = *fmt++;
+ goto do_count;
+ }
if (c == 'l')
c = *fmt++;
if (c == 'l')
c = *fmt++;
+ do_count:
switch (c)
{
case 'p':
@@ -874,6 +977,14 @@ parse_printf_arg_fmt (const char *fmt0, struct printf_args *args,
continue;
}
+ if (c == 'z')
+ {
+ c = *fmt++;
+ if (sizeof (size_t) == sizeof (unsigned long))
+ longfmt = 1;
+ else if (sizeof (size_t) == sizeof (unsigned long long))
+ longfmt = 2;
+ }
if (c == 'l')
{
c = *fmt++;
@@ -1050,6 +1161,8 @@ grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0,
}
c = *fmt++;
+ if (c == 'z')
+ c = *fmt++;
if (c == 'l')
c = *fmt++;
if (c == 'l')
diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
index 027a25cd1..5f769a299 100644
--- a/grub-core/kern/mm.c
+++ b/grub-core/kern/mm.c
@@ -786,6 +786,39 @@ grub_mm_dump (unsigned lineno)
grub_printf ("\n");
}
+void
+grub_mm_dump_regions (void)
+{
+ grub_mm_region_t r;
+ grub_mm_header_t p;
+ grub_size_t num_blocks, sum_free, sum_alloc;
+
+ for (r = grub_mm_base; r; r = r->next)
+ {
+ num_blocks = 0;
+ sum_free = 0;
+ sum_alloc = 0;
+
+ p = (grub_mm_header_t) ALIGN_UP ((grub_addr_t) (r + 1), GRUB_MM_ALIGN);
+ for (; (grub_addr_t) p < (grub_addr_t) (r+1) + r->size; p++, num_blocks++)
+ {
+ switch (p->magic)
+ {
+ case GRUB_MM_FREE_MAGIC:
+ sum_free += p->size;
+ break;
+ case GRUB_MM_ALLOC_MAGIC:
+ sum_alloc += p->size;
+ break;
+ }
+ }
+ grub_printf ("Region %p (size %" PRIuGRUB_SIZE " blocks %" PRIuGRUB_SIZE " free %" PRIuGRUB_SIZE " alloc %" PRIuGRUB_SIZE ")\n\n",
+ r, r->size, num_blocks, sum_free << GRUB_MM_ALIGN_LOG2, sum_alloc << GRUB_MM_ALIGN_LOG2);
+ }
+
+ grub_printf ("\n");
+}
+
void *
grub_debug_calloc (const char *file, int line, grub_size_t nmemb, grub_size_t size)
{
diff --git a/grub-core/kern/powerpc/ieee1275/ieee1275.c b/grub-core/kern/powerpc/ieee1275/ieee1275.c
new file mode 100644
index 000000000..20c49e371
--- /dev/null
+++ b/grub-core/kern/powerpc/ieee1275/ieee1275.c
@@ -0,0 +1,137 @@
+/* ieee1275.c - Access the Open Firmware client interface. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc.
+ * Copyright (C) 2020, 2021, 2022, 2023, 2024, 2025 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
+
+grub_int32_t
+grub_ieee1275_test (const char *interface_name)
+{
+ struct test_args
+ {
+ struct grub_ieee1275_common_hdr common;/* The header information like interface name, number of inputs and outputs. */
+ grub_ieee1275_cell_t name; /* The interface name. */
+ grub_ieee1275_cell_t missing;
+ } args;
+
+ INIT_IEEE1275_COMMON (&args.common, "test", 1, 1);
+ args.name = (grub_ieee1275_cell_t) interface_name;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+
+ if (args.missing == IEEE1275_CELL_INVALID)
+ return -1;
+
+ return 0;
+}
+
+grub_int32_t
+grub_ieee1275_pks_max_object_size (grub_uint32_t *result)
+{
+ struct mos_args
+ {
+ struct grub_ieee1275_common_hdr common;/* The header information like interface name, number of inputs and outputs. */
+ grub_ieee1275_cell_t size; /* The maximum object size for a PKS object. */
+ } args;
+
+ INIT_IEEE1275_COMMON (&args.common, GRUB_PKS_MAX_OBJ_INTERFACE, 0, 1);
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+
+ if (args.size == IEEE1275_CELL_INVALID || args.size == 0)
+ return -1;
+
+ *result = args.size;
+
+ return 0;
+}
+
+grub_int32_t
+grub_ieee1275_pks_read_object (const grub_uint32_t consumer, const char *label,
+ const grub_uint32_t label_len, const grub_uint32_t buffer_len,
+ grub_uint8_t *buffer, grub_uint32_t *data_len,
+ grub_uint32_t *policies)
+{
+ struct pks_read_args
+ {
+ struct grub_ieee1275_common_hdr common; /* The header information like interface name, number of inputs and outputs. */
+ grub_ieee1275_cell_t consumer; /* The object belonging to consumer with the label. */
+ grub_ieee1275_cell_t label; /* Object label buffer logical real address. */
+ grub_ieee1275_cell_t label_len; /* The byte length of the object label. */
+ grub_ieee1275_cell_t buffer; /* Output buffer logical real address. */
+ grub_ieee1275_cell_t buffer_len; /* Length of the output buffer. */
+ grub_ieee1275_cell_t data_len; /* The number of bytes copied to the output buffer. */
+ grub_ieee1275_cell_t policies; /* The object policies. */
+ grub_int32_t rc; /* The return code. */
+ } args;
+
+ INIT_IEEE1275_COMMON (&args.common, GRUB_PKS_READ_OBJ_INTERFACE, 5, 3);
+ args.consumer = consumer;
+ args.label_len = label_len;
+ args.buffer_len = buffer_len;
+ args.label = (grub_ieee1275_cell_t) label;
+ args.buffer = (grub_ieee1275_cell_t) buffer;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+
+ if (args.data_len == IEEE1275_CELL_INVALID)
+ return -1;
+
+ *data_len = args.data_len;
+ *policies = args.policies;
+
+ return args.rc;
+}
+
+grub_int32_t
+grub_ieee1275_pks_read_sbvar (const grub_uint32_t sbvar_flags, const grub_uint32_t sbvar_type,
+ const grub_uint32_t buffer_len, grub_uint8_t *buffer,
+ grub_size_t *data_len)
+{
+ struct pks_read_sbvar_args
+ {
+ struct grub_ieee1275_common_hdr common; /* The header information like interface name, number of inputs and outputs. */
+ grub_ieee1275_cell_t sbvar_flags; /* The sbvar operation flags. */
+ grub_ieee1275_cell_t sbvar_type; /* The sbvar being requested. */
+ grub_ieee1275_cell_t buffer; /* Output buffer logical real address. */
+ grub_ieee1275_cell_t buffer_len; /* Length of the Output buffer. */
+ grub_ieee1275_cell_t data_len; /* The number of bytes copied to the output buffer. */
+ grub_int32_t rc; /* The return code. */
+ } args;
+
+ INIT_IEEE1275_COMMON (&args.common, GRUB_PKS_READ_SBVAR_INTERFACE, 4, 2);
+ args.sbvar_flags = sbvar_flags;
+ args.sbvar_type = sbvar_type;
+ args.buffer_len = buffer_len;
+ args.buffer = (grub_ieee1275_cell_t) buffer;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+
+ if (args.data_len == IEEE1275_CELL_INVALID)
+ return -1;
+
+ *data_len = args.data_len;
+
+ return args.rc;
+}
diff --git a/grub-core/kern/powerpc/ieee1275/platform_keystore.c b/grub-core/kern/powerpc/ieee1275/platform_keystore.c
new file mode 100644
index 000000000..cc2d493bb
--- /dev/null
+++ b/grub-core/kern/powerpc/ieee1275/platform_keystore.c
@@ -0,0 +1,333 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2024 Free Software Foundation, Inc.
+ * Copyright (C) 2022, 2023, 2024, 2025 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
+#include
+#include
+#include
+#include
+
+/* PKS object maximum size. */
+static grub_uint32_t pks_max_object_size = 0;
+
+/* Platform KeyStore db and dbx. */
+static grub_pks_t pks_keystore = { .db = NULL, .dbx = NULL, .db_entries = 0,
+ .dbx_entries = 0, .db_exists = true};
+/*
+ * pks_use_keystore: Key Management Modes
+ * False: Static key management (use built-in Keys). This is default.
+ * True: Dynamic key management (use Platform KeySotre).
+ */
+static bool pks_use_keystore = false;
+
+/*
+ * Reads the Globally Unique Identifier (GUID), EFI Signature Database (ESD),
+ * and its size from the Platform KeyStore EFI Signature List (ESL), then
+ * stores them into the PKS Signature Database (SD) (i.e., pks_sd buffer
+ * and pks_sd entries) in the GRUB.
+ */
+static grub_err_t
+_esl_to_esd (const grub_uint8_t *esl_data, grub_size_t esl_size,
+ const grub_size_t signature_size, const grub_packed_guid_t *guid,
+ grub_pks_sd_t **pks_sd, grub_uint32_t *pks_sd_entries)
+{
+ grub_esd_t *esd;
+ grub_pks_sd_t *signature = *pks_sd;
+ grub_uint32_t entries = *pks_sd_entries;
+ grub_size_t data_size, offset = 0;
+
+ /* Reads the ESD from ESL. */
+ while (esl_size > 0)
+ {
+ esd = (grub_esd_t *) (esl_data + offset);
+ data_size = signature_size - sizeof (grub_esd_t);
+
+ signature = grub_realloc (signature, (entries + 1) * sizeof (grub_pks_sd_t));
+ if (signature == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
+
+ signature[entries].data = grub_malloc (data_size * sizeof (grub_uint8_t));
+ if (signature[entries].data == NULL)
+ {
+ /* Allocated memory will be freed by grub_pks_free_data(). */
+ *pks_sd = signature;
+ *pks_sd_entries = entries + 1;
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
+ }
+
+ grub_memcpy (signature[entries].data, esd->signature_data, data_size);
+ signature[entries].data_size = data_size;
+ signature[entries].guid = *guid;
+ entries++;
+ esl_size -= signature_size;
+ offset += signature_size;
+ }
+
+ *pks_sd = signature;
+ *pks_sd_entries = entries;
+
+ return GRUB_ERR_NONE;
+}
+
+/* Extract the ESD after removing the ESL header from ESL. */
+static grub_err_t
+esl_to_esd (const grub_uint8_t *esl_data, grub_size_t *next_esl,
+ grub_pks_sd_t **pks_sd, grub_uint32_t *pks_sd_entries)
+{
+ grub_packed_guid_t guid;
+ grub_esl_t *esl;
+ grub_size_t offset, esl_size, signature_size, signature_header_size;
+
+ /* Convert the ESL data into the ESL. */
+ esl = (grub_esl_t *) esl_data;
+ if (*next_esl < sizeof (grub_esl_t) || esl == NULL)
+ return grub_error (GRUB_ERR_BUG, "invalid ESL");
+
+ esl_size = grub_le_to_cpu32 (esl->signature_list_size);
+ signature_header_size = grub_le_to_cpu32 (esl->signature_header_size);
+ signature_size = grub_le_to_cpu32 (esl->signature_size);
+ grub_memcpy (&guid, &esl->signature_type, sizeof (grub_packed_guid_t));
+
+ if (esl_size < sizeof (grub_esl_t) || esl_size > *next_esl)
+ return grub_error (GRUB_ERR_BUG, "invalid ESL size (%u)\n", esl_size);
+
+ *next_esl = esl_size;
+ offset = sizeof (grub_esl_t) + signature_header_size;
+ esl_size = esl_size - offset;
+
+ return _esl_to_esd (esl_data + offset, esl_size, signature_size, &guid,
+ pks_sd, pks_sd_entries);
+}
+
+/*
+ * Import the EFI Signature Database (ESD) and the number of ESD from the ESL
+ * into the pks_sd buffer and pks_sd entries.
+ */
+static grub_err_t
+pks_sd_from_esl (const grub_uint8_t *esl_data, grub_size_t esl_size,
+ grub_pks_sd_t **pks_sd, grub_uint32_t *pks_sd_entries)
+{
+ grub_err_t rc;
+ grub_size_t next_esl = esl_size;
+
+ do
+ {
+ rc = esl_to_esd (esl_data, &next_esl, pks_sd, pks_sd_entries);
+ if (rc != GRUB_ERR_NONE)
+ break;
+
+ esl_data += next_esl;
+ esl_size -= next_esl;
+ next_esl = esl_size;
+ }
+ while (esl_size > 0);
+
+ return rc;
+}
+
+/* Read the secure boot version from PKS as an object. Caller must free result. */
+static grub_err_t
+read_sbversion_from_pks (grub_uint8_t **out)
+{
+ grub_int32_t rc;
+ grub_uint32_t outlen = 0, policy = 0;
+
+ *out = grub_malloc (pks_max_object_size);
+ if (*out == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
+
+ rc = grub_ieee1275_pks_read_object (GRUB_PKS_CONSUMER_FW, GRUB_SB_VERSION_KEY_NAME,
+ GRUB_SB_VERSION_KEY_LEN, pks_max_object_size, *out,
+ &outlen, &policy);
+ if (rc < 0)
+ {
+ grub_free (*out);
+ return grub_error (GRUB_ERR_READ_ERROR, "SB version read failed (%d)\n", rc);
+ }
+
+ if (outlen != 1 || (**out >= 2))
+ {
+ grub_free (*out);
+ return grub_error (GRUB_ERR_BAD_NUMBER, "found unexpected SB version: %u\n", **out);
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+/*
+ * Reads the secure boot variable from PKS, unpacks it, read the ESD from ESL,
+ * and store the information in the pks_sd buffer.
+ */
+static grub_err_t
+read_sbvar_from_pks (const grub_uint32_t sbvarflags, const grub_uint32_t sbvartype,
+ grub_pks_sd_t **pks_sd, grub_uint32_t *pks_sd_entries)
+{
+ grub_int32_t rc;
+ grub_err_t err = GRUB_ERR_NONE;
+ grub_uint8_t *esl_data = NULL;
+ grub_size_t esl_data_size = 0;
+
+ esl_data = grub_malloc (pks_max_object_size);
+ if (esl_data == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
+
+ rc = grub_ieee1275_pks_read_sbvar (sbvarflags, sbvartype, pks_max_object_size,
+ esl_data, &esl_data_size);
+ if (rc == IEEE1275_CELL_NOT_FOUND)
+ {
+ err = grub_error (GRUB_ERR_FILE_NOT_FOUND, "secure boot variable %s not found (%d)",
+ (sbvartype == GRUB_PKS_SBVAR_DB) ? "db" : "dbx", rc);
+ goto fail;
+ }
+ else if (rc < 0)
+ {
+ err = grub_error (GRUB_ERR_READ_ERROR, "secure boot variable %s reading (%d)",
+ (sbvartype == GRUB_PKS_SBVAR_DB) ? "db" : "dbx", rc);
+ goto fail;
+ }
+
+ if (esl_data_size > 0)
+ err = pks_sd_from_esl (esl_data, esl_data_size, pks_sd, pks_sd_entries);
+ else
+ err = GRUB_ERR_BAD_NUMBER;
+
+ fail:
+ grub_free (esl_data);
+
+ return err;
+}
+
+/*
+ * Test the availability of PKS support. If PKS support is avaialble and objects
+ * present, it reads the secure boot version (SB_VERSION) from PKS.
+ *
+ * SB_VERSION: Key Management Mode
+ * 1 - Enable dynamic key management mode. Read the db and dbx variables from PKS,
+ * and use them for signature verification.
+ * 0 - Enable static key management mode. Read keys from the GRUB ELF Note and use
+ * it for signature verification.
+ */
+static bool
+is_pks_present (void)
+{
+ grub_err_t err;
+ grub_int32_t rc;
+ grub_uint8_t *data = NULL;
+ bool ret = false;
+
+ rc = grub_ieee1275_test (GRUB_PKS_MAX_OBJ_INTERFACE);
+ if (rc < 0)
+ {
+ grub_error (GRUB_ERR_BAD_FIRMWARE, "firmware doesn't have PKS support\n");
+ return ret;
+ }
+ else
+ {
+ rc = grub_ieee1275_pks_max_object_size (&pks_max_object_size);
+ if (rc < 0)
+ {
+ grub_error (GRUB_ERR_BAD_NUMBER, "PKS support is there but it has zero objects\n");
+ return ret;
+ }
+ }
+
+ err = read_sbversion_from_pks (&data);
+ if (err != GRUB_ERR_NONE)
+ return ret;
+
+ /*
+ * If *data == 1, use dynamic key management and read the keys from the PKS.
+ * Else, use static key management and read the keys from the GRUB ELF Note.
+ */
+ ret = ((*data == 1) ? true : false);
+
+ grub_free (data);
+
+ return ret;
+}
+
+/* Free allocated memory. */
+void
+grub_pks_free_data (void)
+{
+ grub_size_t i;
+
+ for (i = 0; i < pks_keystore.db_entries; i++)
+ grub_free (pks_keystore.db[i].data);
+
+ for (i = 0; i < pks_keystore.dbx_entries; i++)
+ grub_free (pks_keystore.dbx[i].data);
+
+ grub_free (pks_keystore.db);
+ grub_free (pks_keystore.dbx);
+ grub_memset (&pks_keystore, 0, sizeof (grub_pks_t));
+}
+
+grub_pks_t *
+grub_pks_get_keystore (void)
+{
+ return (pks_use_keystore == true) ? &pks_keystore : NULL;
+}
+
+/* Initialization of the Platform KeyStore. */
+void
+grub_pks_keystore_init (void)
+{
+ grub_err_t rc_db, rc_dbx;
+
+ grub_dprintf ("ieee1275", "trying to load Platform KeyStore\n");
+
+ if (is_pks_present () == false)
+ {
+ grub_dprintf ("ieee1275", "Platform PKS is not available\n");
+ return;
+ }
+
+ /*
+ * When read db from PKS, there are three scenarios
+ * 1. db fully loaded from PKS
+ * 2. db partially loaded from PKS
+ * 3. no keys are loaded from db (if db does not exist in PKS), default to
+ * built-in keys (static keys)
+ * each of these scenarios, the db keys are checked against dbx.
+ */
+ rc_db = read_sbvar_from_pks (0, GRUB_PKS_SBVAR_DB, &pks_keystore.db, &pks_keystore.db_entries);
+ if (rc_db == GRUB_ERR_FILE_NOT_FOUND)
+ pks_keystore.db_exists = false;
+
+ /*
+ * Read dbx from PKS. If dbx is not completely loaded from PKS, then this
+ * could lead to the loading of vulnerable GRUB modules and kernel binaries.
+ * So, this should be prevented by freeing up loaded dbx and db.
+ */
+ rc_dbx = read_sbvar_from_pks (0, GRUB_PKS_SBVAR_DBX, &pks_keystore.dbx, &pks_keystore.dbx_entries);
+ if (rc_dbx == GRUB_ERR_FILE_NOT_FOUND || rc_dbx == GRUB_ERR_BAD_NUMBER)
+ rc_dbx = GRUB_ERR_NONE;
+
+ if (rc_dbx != GRUB_ERR_NONE)
+ grub_pks_free_data ();
+
+ /*
+ * At this point, it's evident that PKS infrastructure exists, so the PKS
+ * keystore must be used for validating appended signatures.
+ */
+ pks_use_keystore = true;
+}
diff --git a/grub-core/kern/xen/cmdline.c b/grub-core/kern/xen/cmdline.c
new file mode 100644
index 000000000..bdea89ecc
--- /dev/null
+++ b/grub-core/kern/xen/cmdline.c
@@ -0,0 +1,360 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2025 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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+enum splitter_state
+{
+ SPLITTER_NORMAL = 0x0,
+ SPLITTER_HIT_BACKSLASH = 0x1,
+ SPLITTER_IN_SINGLE_QUOTES = 0x2,
+ SPLITTER_IN_DOUBLE_QUOTES = 0x4,
+};
+typedef enum splitter_state splitter_state_t;
+
+/*
+ * The initial size of the current_word buffer. The buffer may be resized as
+ * needed.
+ */
+#define PARSER_BASE_WORD_SIZE 32
+
+struct parser_state
+{
+ char **words;
+ grub_size_t words_count;
+ char *current_word;
+ grub_size_t current_word_len;
+ grub_size_t current_word_pos;
+};
+typedef struct parser_state parser_state_t;
+
+static grub_err_t
+append_char_to_word (parser_state_t *ps, char c, bool allow_null)
+{
+ /*
+ * We ban any chars that are not in the ASCII printable range. If
+ * allow_null == true, we make an exception for NUL. (This is needed so that
+ * append_word_to_list can add a NUL terminator to the word).
+ */
+ if (!grub_isprint (c) && allow_null == false)
+ return GRUB_ERR_BAD_ARGUMENT;
+ else if (allow_null == true && c != '\0')
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ if (ps->current_word_pos == ps->current_word_len)
+ {
+ ps->current_word = grub_realloc (ps->current_word, ps->current_word_len *= 2);
+ if (ps->current_word == NULL)
+ {
+ ps->current_word_len /= 2;
+ return grub_errno;
+ }
+ }
+
+ ps->current_word[ps->current_word_pos++] = c;
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+append_word_to_list (parser_state_t *ps)
+{
+ /* No-op on empty words. */
+ if (ps->current_word_pos == 0)
+ return GRUB_ERR_NONE;
+
+ if (append_char_to_word (ps, '\0', true) != GRUB_ERR_NONE)
+ grub_fatal ("couldn't append NUL terminator to word during Xen cmdline parsing");
+
+ ps->current_word_len = grub_strlen (ps->current_word) + 1;
+ ps->current_word = grub_realloc (ps->current_word, ps->current_word_len);
+ if (ps->current_word == NULL)
+ return grub_errno;
+ ps->words = grub_realloc (ps->words, ++ps->words_count * sizeof (char *));
+ if (ps->words == NULL)
+ return grub_errno;
+ ps->words[ps->words_count - 1] = ps->current_word;
+
+ ps->current_word_len = PARSER_BASE_WORD_SIZE;
+ ps->current_word_pos = 0;
+ ps->current_word = grub_malloc (ps->current_word_len);
+ if (ps->current_word == NULL)
+ return grub_errno;
+
+ return GRUB_ERR_NONE;
+}
+
+static bool
+is_key_safe (char *key, grub_size_t len)
+{
+ grub_size_t i;
+
+ for (i = 0; i < len; i++)
+ if (!grub_isalpha (key[i]) && key[i] != '_')
+ return false;
+
+ return true;
+}
+
+void
+grub_parse_xen_cmdline (void)
+{
+ parser_state_t ps = {0};
+ splitter_state_t ss = SPLITTER_NORMAL;
+
+ const char *cmdline = (const char *) grub_xen_start_page_addr->cmd_line;
+ grub_size_t cmdline_len;
+ bool cmdline_valid = false;
+ char **param_keys = NULL;
+ char **param_vals = NULL;
+ grub_size_t param_dict_len = 0;
+ grub_size_t param_dict_pos = 0;
+ char current_char = '\0';
+ grub_size_t i = 0;
+
+ /*
+ * The following algorithm is used to parse the Xen command line:
+ *
+ * - The command line is split into space-separated words.
+ * - Single and double quotes may be used to suppress the splitting
+ * behavior of spaces.
+ * - Double quotes are appended to the current word verbatim if they
+ * appear within a single-quoted string portion, and vice versa.
+ * - Backslashes may be used to cause the next character to be
+ * appended to the current word verbatim. This is only useful when
+ * used to escape quotes, spaces, and backslashes, but for simplicity
+ * we allow backslash-escaping anything.
+ * - After splitting the command line into words, each word is checked to
+ * see if it contains an equals sign.
+ * - If it does, it is split on the equals sign into a key-value pair. The
+ * key is then treated as an variable name, and the value is treated as
+ * the variable's value.
+ * - If it does not, the entire word is treated as a variable name. The
+ * variable's value is implicitly considered to be `1`.
+ * - All variables detected on the command line are checked to see if their
+ * names begin with the string `xen_grub_env_`. Variables that do not pass
+ * this check are discarded, variables that do pass this check are
+ * exported so they are available to the GRUB configuration.
+ *
+ * This behavior is intended to somewhat mimic the splitter behavior in Bash
+ * and in GRUB's config file parser.
+ */
+
+ ps.current_word_len = PARSER_BASE_WORD_SIZE;
+ ps.current_word = grub_malloc (ps.current_word_len);
+ if (ps.current_word == NULL)
+ goto cleanup_main;
+
+ for (i = 0; i < GRUB_XEN_MAX_GUEST_CMDLINE; i++)
+ {
+ if (cmdline[i] == '\0')
+ {
+ cmdline_valid = true;
+ break;
+ }
+ }
+
+ if (cmdline_valid == false)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "command line from Xen is not NUL-terminated");
+ grub_print_error ();
+ goto cleanup_main;
+ }
+
+ cmdline_len = grub_strlen (cmdline);
+ for (i = 0; i < cmdline_len; i++)
+ {
+ current_char = cmdline[i];
+
+ /*
+ * If the previous character was a backslash, append the current
+ * character to the word verbatim
+ */
+ if (ss & SPLITTER_HIT_BACKSLASH)
+ {
+ ss &= ~SPLITTER_HIT_BACKSLASH;
+ if (append_char_to_word (&ps, current_char, false) != GRUB_ERR_NONE)
+ goto cleanup_main;
+ continue;
+ }
+
+ switch (current_char)
+ {
+ case '\\':
+ /* Backslashes escape arbitrary characters. */
+ ss |= SPLITTER_HIT_BACKSLASH;
+ break;
+
+ case '\'':
+ /*
+ * Single quotes suppress word splitting and double quoting until
+ * the next single quote is encountered.
+ */
+ if (ss & SPLITTER_IN_DOUBLE_QUOTES)
+ {
+ if (append_char_to_word (&ps, current_char, false) != GRUB_ERR_NONE)
+ goto cleanup_main;
+ break;
+ }
+
+ ss ^= SPLITTER_IN_SINGLE_QUOTES;
+ break;
+
+ case '"':
+ /*
+ * Double quotes suppress word splitting and single quoting until
+ * the next double quote is encountered.
+ */
+ if (ss & SPLITTER_IN_SINGLE_QUOTES)
+ {
+ if (append_char_to_word (&ps, current_char, false) != GRUB_ERR_NONE)
+ goto cleanup_main;
+ break;
+ }
+
+ ss ^= SPLITTER_IN_DOUBLE_QUOTES;
+ break;
+
+ case ' ':
+ /* Spaces separate words in the command line from each other. */
+ if (ss & SPLITTER_IN_SINGLE_QUOTES ||
+ ss & SPLITTER_IN_DOUBLE_QUOTES)
+ {
+ if (append_char_to_word (&ps, current_char, false) != GRUB_ERR_NONE)
+ goto cleanup_main;
+ break;
+ }
+
+ if (append_word_to_list (&ps) != GRUB_ERR_NONE)
+ goto cleanup_main;
+ break;
+
+ default:
+ if (append_char_to_word (&ps, current_char, false) != GRUB_ERR_NONE)
+ goto cleanup_main;
+ }
+ }
+
+ if (append_word_to_list (&ps) != GRUB_ERR_NONE)
+ goto cleanup_main;
+
+ param_keys = grub_malloc (ps.words_count * sizeof (char *));
+ if (param_keys == NULL)
+ goto cleanup_main;
+ param_vals = grub_malloc (ps.words_count * sizeof (char *));
+ if (param_vals == NULL)
+ goto cleanup_main;
+
+ for (i = 0; i < ps.words_count; i++)
+ {
+ char *eq_pos;
+
+ ps.current_word = ps.words[i];
+ ps.current_word_len = grub_strlen (ps.current_word) + 1;
+ eq_pos = grub_strchr (ps.current_word, '=');
+
+ if (eq_pos != NULL)
+ {
+ /*
+ * Both pre_eq_len and post_eq_len represent substring lengths
+ * without a NUL terminator.
+ */
+ grub_size_t pre_eq_len = (grub_size_t) (eq_pos - ps.current_word);
+ /*
+ * ps.current_word_len includes the NUL terminator, so we subtract
+ * one to get rid of the terminator, and one more to get rid of the
+ * equals sign.
+ */
+ grub_size_t post_eq_len = (ps.current_word_len - 2) - pre_eq_len;
+
+ if (is_key_safe (ps.current_word, pre_eq_len) == true)
+ {
+ param_dict_pos = param_dict_len++;
+ param_keys[param_dict_pos] = grub_malloc (pre_eq_len + 1);
+ if (param_keys == NULL)
+ goto cleanup_main;
+ param_vals[param_dict_pos] = grub_malloc (post_eq_len + 1);
+ if (param_vals == NULL)
+ goto cleanup_main;
+
+ grub_strncpy (param_keys[param_dict_pos], ps.current_word, pre_eq_len);
+ grub_strncpy (param_vals[param_dict_pos],
+ ps.current_word + pre_eq_len + 1, post_eq_len);
+ param_keys[param_dict_pos][pre_eq_len] = '\0';
+ param_vals[param_dict_pos][post_eq_len] = '\0';
+ }
+ }
+ else if (is_key_safe (ps.current_word, ps.current_word_len - 1) == true)
+ {
+ param_dict_pos = param_dict_len++;
+ param_keys[param_dict_pos] = grub_malloc (ps.current_word_len);
+ if (param_keys == NULL)
+ goto cleanup_main;
+ param_vals[param_dict_pos] = grub_zalloc (2);
+ if (param_vals == NULL)
+ goto cleanup_main;
+
+ grub_strncpy (param_keys[param_dict_pos], ps.current_word,
+ ps.current_word_len);
+ if (param_keys[param_dict_pos][ps.current_word_len - 1] != '\0' )
+ grub_fatal ("NUL terminator missing from key during Xen cmdline parsing");
+ *param_vals[param_dict_pos] = '1';
+ }
+ }
+
+ for (i = 0; i < param_dict_len; i++)
+ {
+ /*
+ * Find keys that start with "xen_grub_env_" and export them
+ * as environment variables.
+ */
+ if (grub_strncmp (param_keys[i],
+ "xen_grub_env_",
+ sizeof ("xen_grub_env_") - 1) != 0)
+ continue;
+
+ if (grub_env_set (param_keys[i], param_vals[i]) != GRUB_ERR_NONE)
+ {
+ grub_printf ("warning: could not set environment variable `%s' to value `%s'\n",
+ param_keys[i], param_vals[i]);
+ continue;
+ }
+
+ if (grub_env_export (param_keys[i]) != GRUB_ERR_NONE)
+ grub_printf ("warning: could not export environment variable `%s'",
+ param_keys[i]);
+ }
+
+ cleanup_main:
+ for (i = 0; i < ps.words_count; i++)
+ grub_free (ps.words[i]);
+
+ for (i = 0; i < param_dict_len; i++)
+ {
+ grub_free (param_keys[i]);
+ grub_free (param_vals[i]);
+ }
+
+ grub_free (param_keys);
+ grub_free (param_vals);
+ grub_free (ps.words);
+}
diff --git a/grub-core/kern/xen/init.c b/grub-core/kern/xen/init.c
index 782ca7295..69cf59f0c 100644
--- a/grub-core/kern/xen/init.c
+++ b/grub-core/kern/xen/init.c
@@ -581,6 +581,8 @@ grub_machine_init (void)
grub_xendisk_init ();
grub_boot_init ();
+
+ grub_parse_xen_cmdline ();
}
void
diff --git a/grub-core/lib/argon2.c b/grub-core/lib/argon2.c
new file mode 100644
index 000000000..12ad7ad1c
--- /dev/null
+++ b/grub-core/lib/argon2.c
@@ -0,0 +1,52 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2025 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 .
+ */
+
+#include
+#include
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+gcry_err_code_t
+grub_crypto_argon2 (int subalgo,
+ const unsigned long *param, unsigned int paramlen,
+ const void *password, grub_size_t passwordlen,
+ const void *salt, grub_size_t saltlen,
+ const void *key, grub_size_t keylen,
+ const void *ad, grub_size_t adlen,
+ grub_size_t resultlen, void *result)
+{
+ gcry_kdf_hd_t hd = {0};
+ gpg_err_code_t err;
+
+ if (saltlen == 0)
+ return GPG_ERR_INV_VALUE;
+
+ err = _gcry_kdf_open (&hd, GRUB_GCRY_KDF_ARGON2, subalgo, param, paramlen,
+ password, passwordlen, salt, saltlen, key, keylen,
+ ad, adlen);
+ if (err != GPG_ERR_NO_ERROR)
+ return err;
+
+ err = _gcry_kdf_compute (hd, NULL);
+ if (err == GPG_ERR_NO_ERROR)
+ err = _gcry_kdf_final (hd, resultlen, result);
+
+ _gcry_kdf_close (hd);
+
+ return err;
+}
diff --git a/grub-core/lib/b64dec.c b/grub-core/lib/b64dec.c
index 868d98568..769cabda4 100644
--- a/grub-core/lib/b64dec.c
+++ b/grub-core/lib/b64dec.c
@@ -100,8 +100,8 @@ _gpgrt_b64dec_start (const char *title)
/* Do in-place decoding of base-64 data of LENGTH in BUFFER. Stores the
new length of the buffer at R_NBYTES. */
gpg_err_code_t
-_gpgrt_b64dec_proc (gpgrt_b64state_t state, void *buffer, size_t length,
- size_t *r_nbytes)
+_gpgrt_b64dec_proc (gpgrt_b64state_t state, void *buffer, grub_size_t length,
+ grub_size_t *r_nbytes)
{
enum decoder_states ds = state->idx;
unsigned char val = state->radbuf[0];
diff --git a/grub-core/lib/crypto.c b/grub-core/lib/crypto.c
index dd60dd4ac..c29eeb333 100644
--- a/grub-core/lib/crypto.c
+++ b/grub-core/lib/crypto.c
@@ -31,7 +31,9 @@ struct grub_crypto_hmac_handle
{
const struct gcry_md_spec *md;
void *ctx;
- void *opad;
+ void *ctx2;
+ void *ctx_cache;
+ void *ctx2_cache;
};
static gcry_cipher_spec_t *grub_ciphers = NULL;
@@ -170,6 +172,10 @@ grub_md_unregister (gcry_md_spec_t *cipher)
}
}
+struct gcry_pk_spec *grub_crypto_pk_dsa;
+struct gcry_pk_spec *grub_crypto_pk_ecdsa;
+struct gcry_pk_spec *grub_crypto_pk_rsa;
+
void
grub_crypto_hash (const gcry_md_spec_t *hash, void *out, const void *in,
grub_size_t inlen)
@@ -439,7 +445,8 @@ grub_crypto_hmac_init (const struct gcry_md_spec *md,
{
grub_uint8_t *helpkey = NULL;
grub_uint8_t *ipad = NULL, *opad = NULL;
- void *ctx = NULL;
+ void *ctx = NULL, *ctx2 = NULL;
+ void *ctx_cache = NULL, *ctx2_cache = NULL;
struct grub_crypto_hmac_handle *ret = NULL;
unsigned i;
@@ -450,6 +457,18 @@ grub_crypto_hmac_init (const struct gcry_md_spec *md,
if (!ctx)
goto err;
+ ctx2 = grub_malloc (md->contextsize);
+ if (!ctx2)
+ goto err;
+
+ ctx_cache = grub_malloc (md->contextsize);
+ if (!ctx_cache)
+ goto err;
+
+ ctx2_cache = grub_malloc (md->contextsize);
+ if (!ctx2_cache)
+ goto err;
+
if ( keylen > md->blocksize )
{
helpkey = grub_malloc (md->mdlen);
@@ -479,26 +498,40 @@ grub_crypto_hmac_init (const struct gcry_md_spec *md,
grub_free (helpkey);
helpkey = NULL;
+ /* inner pad */
md->init (ctx, 0);
-
- md->write (ctx, ipad, md->blocksize); /* inner pad */
+ md->write (ctx, ipad, md->blocksize);
+ grub_memcpy (ctx_cache, ctx, md->contextsize);
grub_memset (ipad, 0, md->blocksize);
grub_free (ipad);
ipad = NULL;
+ /* outer pad */
+ md->init (ctx2, 0);
+ md->write (ctx2, opad, md->blocksize);
+ grub_memcpy (ctx2_cache, ctx2, md->contextsize);
+ grub_memset (opad, 0, md->blocksize);
+ grub_free (opad);
+ opad = NULL;
+
ret = grub_malloc (sizeof (*ret));
if (!ret)
goto err;
ret->md = md;
ret->ctx = ctx;
- ret->opad = opad;
+ ret->ctx2 = ctx2;
+ ret->ctx_cache = ctx_cache;
+ ret->ctx2_cache = ctx2_cache;
return ret;
err:
grub_free (helpkey);
grub_free (ctx);
+ grub_free (ctx2);
+ grub_free (ctx_cache);
+ grub_free (ctx2_cache);
grub_free (ipad);
grub_free (opad);
return NULL;
@@ -512,37 +545,48 @@ grub_crypto_hmac_write (struct grub_crypto_hmac_handle *hnd,
hnd->md->write (hnd->ctx, data, datalen);
}
-gcry_err_code_t
+void
grub_crypto_hmac_fini (struct grub_crypto_hmac_handle *hnd, void *out)
{
- grub_uint8_t *p;
- grub_uint8_t *ctx2;
+ grub_crypto_hmac_final (hnd, out);
+ grub_crypto_hmac_free (hnd);
+}
- ctx2 = grub_malloc (hnd->md->contextsize);
- if (!ctx2)
- return GPG_ERR_OUT_OF_MEMORY;
+void
+grub_crypto_hmac_reset (struct grub_crypto_hmac_handle *hnd)
+{
+ grub_memcpy (hnd->ctx, hnd->ctx_cache, hnd->md->contextsize);
+ grub_memcpy (hnd->ctx2, hnd->ctx2_cache, hnd->md->contextsize);
+}
+
+void
+grub_crypto_hmac_final (struct grub_crypto_hmac_handle *hnd, void *out)
+{
+ grub_uint8_t *p;
hnd->md->final (hnd->ctx);
hnd->md->read (hnd->ctx);
p = hnd->md->read (hnd->ctx);
- hnd->md->init (ctx2, 0);
- hnd->md->write (ctx2, hnd->opad, hnd->md->blocksize);
- hnd->md->write (ctx2, p, hnd->md->mdlen);
- hnd->md->final (ctx2);
- grub_memset (hnd->opad, 0, hnd->md->blocksize);
- grub_free (hnd->opad);
+ hnd->md->write (hnd->ctx2, p, hnd->md->mdlen);
+ hnd->md->final (hnd->ctx2);
+
+ grub_memcpy (out, hnd->md->read (hnd->ctx2), hnd->md->mdlen);
+}
+
+void
+grub_crypto_hmac_free (struct grub_crypto_hmac_handle *hnd)
+{
grub_memset (hnd->ctx, 0, hnd->md->contextsize);
grub_free (hnd->ctx);
-
- grub_memcpy (out, hnd->md->read (ctx2), hnd->md->mdlen);
- grub_memset (ctx2, 0, hnd->md->contextsize);
- grub_free (ctx2);
-
+ grub_memset (hnd->ctx2, 0, hnd->md->contextsize);
+ grub_free (hnd->ctx2);
+ grub_memset (hnd->ctx_cache, 0, hnd->md->contextsize);
+ grub_free (hnd->ctx_cache);
+ grub_memset (hnd->ctx2_cache, 0, hnd->md->contextsize);
+ grub_free (hnd->ctx2_cache);
grub_memset (hnd, 0, sizeof (*hnd));
grub_free (hnd);
-
- return GPG_ERR_NO_ERROR;
}
gcry_err_code_t
@@ -557,7 +601,8 @@ grub_crypto_hmac_buffer (const struct gcry_md_spec *md,
return GPG_ERR_OUT_OF_MEMORY;
grub_crypto_hmac_write (hnd, data, datalen);
- return grub_crypto_hmac_fini (hnd, out);
+ grub_crypto_hmac_fini (hnd, out);
+ return GPG_ERR_NO_ERROR;
}
diff --git a/grub-core/lib/datetime.c b/grub-core/lib/datetime.c
index 8f0922fb0..dbce17e03 100644
--- a/grub-core/lib/datetime.c
+++ b/grub-core/lib/datetime.c
@@ -64,7 +64,10 @@ grub_get_weekday_name (struct grub_datetime *datetime)
#define SECPERDAY (24*SECPERHOUR)
#define DAYSPERYEAR 365
#define DAYSPER4YEARS (4*DAYSPERYEAR+1)
-
+/* 24 leap years in 100 years */
+#define DAYSPER100YEARS (100 * DAYSPERYEAR + 24)
+/* 97 leap years in 400 years */
+#define DAYSPER400YEARS (400 * DAYSPERYEAR + 97)
void
grub_unixtime2datetime (grub_int64_t nix, struct grub_datetime *datetime)
@@ -76,10 +79,12 @@ grub_unixtime2datetime (grub_int64_t nix, struct grub_datetime *datetime)
/* Convenience: let's have 3 consecutive non-bissextile years
at the beginning of the counting date. So count from 1901. */
int days_epoch;
- /* Number of days since 1st Januar, 1901. */
+ /* Number of days since 1st January, 1 (proleptic). */
unsigned days;
/* Seconds into current day. */
unsigned secs_in_day;
+ /* Tracks whether this is a leap year. */
+ bool bisextile;
/* Transform C divisions and modulos to mathematical ones */
if (nix < 0)
@@ -92,27 +97,63 @@ grub_unixtime2datetime (grub_int64_t nix, struct grub_datetime *datetime)
days_epoch = grub_divmod64 (nix, SECPERDAY, NULL);
secs_in_day = nix - days_epoch * SECPERDAY;
- days = days_epoch + 69 * DAYSPERYEAR + 17;
+ /*
+ * 1970 is Unix Epoch. Adjust to a year 1 epoch:
+ * Leap year logic:
+ * - Years evenly divisible by 400 are leap years
+ * - Otherwise, if divisible by 100 are not leap years
+ * - Otherwise, if divisible by 4 are leap years
+ * There are four 400-year periods (1600 years worth of days with leap days)
+ * There are 369 years in addition to the four 400 year periods
+ * There are three 100-year periods worth of leap days (3*24)
+ * There are 17 leap days in 69 years (beyond the three 100 year periods)
+ */
+ days = 4 * DAYSPER400YEARS + 369 * DAYSPERYEAR + 3 * 24 + 17 + days_epoch;
- datetime->year = 1901 + 4 * (days / DAYSPER4YEARS);
+ datetime->year = 1 + 400 * (days / DAYSPER400YEARS);
+ days %= DAYSPER400YEARS;
+
+ /*
+ * On 31st December of bissextile (leap) years 365 days from the beginning
+ * of the year elapsed but year isn't finished yet - every 400 years
+ * 396 is 4 years less than 400 year leap cycle
+ * 96 is 1 day less than number of leap days in 400 years
+ */
+ if (days / DAYSPER100YEARS == 4)
+ {
+ datetime->year += 396;
+ days -= 396 * DAYSPERYEAR + 96;
+ }
+ else
+ {
+ datetime->year += 100 * (days / DAYSPER100YEARS);
+ days %= DAYSPER100YEARS;
+ }
+
+ datetime->year += 4 * (days / DAYSPER4YEARS);
days %= DAYSPER4YEARS;
- /* On 31st December of bissextile years 365 days from the beginning
- of the year elapsed but year isn't finished yet */
+ /*
+ * On 31st December of bissextile (leap) years 365 days from the beginning
+ * of the year elapsed but year isn't finished yet - every 4 years
+ */
if (days / DAYSPERYEAR == 4)
{
datetime->year += 3;
- days -= 3*DAYSPERYEAR;
+ days -= 3 * DAYSPERYEAR;
}
else
{
datetime->year += days / DAYSPERYEAR;
days %= DAYSPERYEAR;
}
- for (i = 0; i < 12
- && days >= (i==1 && datetime->year % 4 == 0
- ? 29 : months[i]); i++)
- days -= (i==1 && datetime->year % 4 == 0
- ? 29 : months[i]);
+
+ bisextile = (datetime->year % 4 == 0
+ && (datetime->year % 100 != 0
+ || datetime->year % 400 == 0)) ? true : false;
+ for (i = 0;
+ i < 12 && days >= ((i == 1 && bisextile == true) ? 29 : months[i]);
+ i++)
+ days -= ((i == 1 && bisextile == true) ? 29 : months[i]);
datetime->month = i + 1;
datetime->day = 1 + days;
datetime->hour = (secs_in_day / SECPERHOUR);
diff --git a/grub-core/lib/efi/tcg2.c b/grub-core/lib/efi/tcg2.c
index 841bf50bb..215d2d7b6 100644
--- a/grub-core/lib/efi/tcg2.c
+++ b/grub-core/lib/efi/tcg2.c
@@ -22,6 +22,7 @@
#include
#include
+#include
#include
static grub_err_t
@@ -141,3 +142,42 @@ grub_tcg2_submit_command (grub_size_t input_size,
return GRUB_ERR_NONE;
}
+
+grub_err_t
+grub_tcg2_cap_pcr (grub_uint8_t pcr)
+{
+ grub_err_t err;
+ grub_efi_status_t status;
+ grub_efi_tpm2_protocol_t *protocol;
+ EFI_TCG2_EVENT *event;
+ grub_uint8_t separator[4] = {0};
+
+ if (pcr >= TPM_MAX_PCRS)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ err = tcg2_get_protocol (&protocol);
+ if (err != GRUB_ERR_NONE)
+ return err;
+
+ event = grub_zalloc (sizeof (EFI_TCG2_EVENT) + sizeof (separator));
+ if (event == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ N_("cannot allocate TPM event buffer"));
+
+ event->Header.HeaderSize = sizeof (EFI_TCG2_EVENT_HEADER);
+ event->Header.HeaderVersion = 1;
+ event->Header.PCRIndex = pcr;
+ event->Header.EventType = GRUB_EV_SEPARATOR;
+ event->Size = sizeof (*event) - sizeof (event->Event) + sizeof (separator);
+ grub_memcpy (event->Event, separator, sizeof (separator));
+
+ status = protocol->hash_log_extend_event (protocol, 0,
+ (grub_addr_t) separator,
+ sizeof (separator), event);
+ grub_free (event);
+
+ if (status != GRUB_EFI_SUCCESS)
+ return grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot cap PCR %u"), pcr);
+
+ return GRUB_ERR_NONE;
+}
diff --git a/grub-core/lib/gnulib-patches/fix-regcomp-resource-leak.patch b/grub-core/lib/gnulib-patches/fix-regcomp-resource-leak.patch
index b2e900023..62e7623a6 100644
--- a/grub-core/lib/gnulib-patches/fix-regcomp-resource-leak.patch
+++ b/grub-core/lib/gnulib-patches/fix-regcomp-resource-leak.patch
@@ -50,7 +50,7 @@
- return REG_NOERROR;
+ return err;
}
-
+
/* If it is possible to do searching in single byte encoding instead of UTF-8
@@ -1677,12 +1677,11 @@ calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, Idx node, bool root)
{
@@ -106,5 +106,5 @@
+
+ return err;
}
-
+
/* Functions for token which are used in the parser. */
diff --git a/grub-core/lib/gnulib-patches/fix-unused-value.patch b/grub-core/lib/gnulib-patches/fix-unused-value.patch
index ba51f1bf2..b56ab4bb4 100644
--- a/grub-core/lib/gnulib-patches/fix-unused-value.patch
+++ b/grub-core/lib/gnulib-patches/fix-unused-value.patch
@@ -1,6 +1,6 @@
--- a/lib/regexec.c 2020-10-21 14:25:35.310195912 +0000
+++ b/lib/regexec.c 2020-10-21 14:32:07.961765604 +0000
-@@ -828,7 +828,11 @@
+@@ -807,7 +807,11 @@
break;
if (__glibc_unlikely (err != REG_NOMATCH))
goto free_return;
diff --git a/grub-core/lib/hwfeatures-gcry.c b/grub-core/lib/hwfeatures-gcry.c
new file mode 100644
index 000000000..acfa7be33
--- /dev/null
+++ b/grub-core/lib/hwfeatures-gcry.c
@@ -0,0 +1,52 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2025 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 .
+ */
+
+#include
+
+#include
+#if defined (__x86_64__) && defined (GRUB_MACHINE_EFI)
+#include
+#endif
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static bool __gcry_use_hwf = false;
+
+bool
+grub_gcry_hwf_enabled (void)
+{
+ return __gcry_use_hwf;
+}
+
+void
+grub_enable_gcry_hwf (void)
+{
+#if defined (__x86_64__) && defined (GRUB_MACHINE_EFI)
+ grub_enable_gcry_hwf_x86_64_efi ();
+#endif
+ __gcry_use_hwf = true;
+}
+
+void
+grub_reset_gcry_hwf (void)
+{
+#if defined (__x86_64__) && defined (GRUB_MACHINE_EFI)
+ grub_reset_gcry_hwf_x86_64_efi ();
+#endif
+ __gcry_use_hwf = false;
+}
diff --git a/grub-core/lib/i386/relocator_common_c.c b/grub-core/lib/i386/relocator_common_c.c
index 7be609b73..c9c5efa72 100644
--- a/grub-core/lib/i386/relocator_common_c.c
+++ b/grub-core/lib/i386/relocator_common_c.c
@@ -26,6 +26,8 @@
#include
#include
+#include
+
extern grub_uint8_t grub_relocator_forward_start;
extern grub_uint8_t grub_relocator_forward_end;
extern grub_uint8_t grub_relocator_backward_start;
@@ -41,20 +43,122 @@ extern grub_size_t grub_relocator_forward_chunk_size;
#define RELOCATOR_SIZEOF(x) (&grub_relocator##x##_end - &grub_relocator##x##_start)
-grub_size_t grub_relocator_align = 1;
grub_size_t grub_relocator_forward_size;
grub_size_t grub_relocator_backward_size;
+grub_size_t grub_relocator_preamble_size = 0;
#ifdef __x86_64__
grub_size_t grub_relocator_jumper_size = 12;
#else
grub_size_t grub_relocator_jumper_size = 7;
#endif
+#if defined(__x86_64__) && defined(GRUB_MACHINE_EFI)
+grub_size_t grub_relocator_align = 4096;
+#else
+grub_size_t grub_relocator_align = 1;
+#endif
+
+#if defined(__x86_64__) && defined(GRUB_MACHINE_EFI)
+
+#define PAGE_PRESENT 1
+#define PAGE_WRITABLE 2
+#define PAGE_USER 4
+#define PAGE_PS 0x80
+#define PAGE_IDX_SIZE 9
+#define PAGE_PS_SHIFT 21
+#define PAGE_NUM_ENTRIES 0x200
+#define PS_PAGE_SIZE 0x200000
+
+static grub_uint64_t max_ram_size;
+
+ /* Helper for grub_get_multiboot_mmap_count. */
+static int
+max_hook (grub_uint64_t addr,
+ grub_uint64_t size,
+ grub_memory_type_t type __attribute__ ((unused)),
+ void *data __attribute__ ((unused)))
+{
+ max_ram_size = grub_max (max_ram_size, addr + size);
+ return 0;
+}
+
+static grub_uint64_t
+find_max_size (void)
+{
+ if (!max_ram_size)
+ {
+ /* We need to map the first 4GiB of address space as well as all the
+ available RAM, so start with 4GiB and increase if we see any RAM
+ above this. */
+ max_ram_size = 1ULL << 32;
+
+ grub_mmap_iterate (max_hook, NULL);
+ }
+
+ return max_ram_size;
+}
+
+void
+grub_cpu_relocator_preamble (void *rels)
+{
+ grub_uint64_t nentries = (find_max_size () + PS_PAGE_SIZE - 1) >> PAGE_PS_SHIFT;
+ grub_uint64_t npt2pages = (nentries + PAGE_NUM_ENTRIES - 1) >> PAGE_IDX_SIZE;
+ grub_uint64_t npt3pages = (npt2pages + PAGE_NUM_ENTRIES - 1) >> PAGE_IDX_SIZE;
+ grub_uint8_t *p = rels;
+ grub_uint64_t *pt4 = (grub_uint64_t *) (p + GRUB_PAGE_SIZE);
+ grub_uint64_t *pt3 = pt4 + PAGE_NUM_ENTRIES;
+ grub_uint64_t *pt2 = pt3 + (npt3pages << PAGE_IDX_SIZE);
+ grub_uint64_t *endpreamble = pt2 + (npt2pages << PAGE_IDX_SIZE);
+ grub_uint64_t i;
+
+ /* movabs $pt4, %rax. */
+ *p++ = 0x48;
+ *p++ = 0xb8;
+ *(grub_uint64_t *) p = (grub_uint64_t) pt4;
+ p += 8;
+
+ /* mov %rax, %cr3. */
+ *p++ = 0x0f;
+ *p++ = 0x22;
+ *p++ = 0xd8;
+
+ /* jmp $endpreamble. */
+ *p++ = 0xe9;
+ *(grub_uint32_t *) p = (grub_uint8_t *) endpreamble - p - 4;
+
+ for (i = 0; i < npt3pages; i++)
+ pt4[i] = ((grub_uint64_t) pt3 + (i << GRUB_PAGE_SHIFT)) | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER;
+
+ for (i = 0; i < npt2pages; i++)
+ pt3[i] = ((grub_uint64_t) pt2 + (i << GRUB_PAGE_SHIFT)) | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER;
+
+ for (i = 0; i < (npt2pages << PAGE_IDX_SIZE); i++)
+ pt2[i] = (i << PAGE_PS_SHIFT) | PAGE_PS | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER;
+}
+
+static void
+compute_preamble_size (void)
+{
+ grub_uint64_t nentries = (find_max_size () + PS_PAGE_SIZE - 1) >> PAGE_PS_SHIFT;
+ grub_uint64_t npt2pages = (nentries + PAGE_NUM_ENTRIES - 1) >> PAGE_IDX_SIZE;
+ grub_uint64_t npt3pages = (npt2pages + PAGE_NUM_ENTRIES - 1) >> PAGE_IDX_SIZE;
+ grub_relocator_preamble_size = (npt2pages + npt3pages + 1 + 1) << GRUB_PAGE_SHIFT;
+}
+
+#else
+void
+grub_cpu_relocator_preamble (void *rels __attribute__((unused)))
+{
+}
+#endif
void
grub_cpu_relocator_init (void)
{
grub_relocator_forward_size = RELOCATOR_SIZEOF (_forward);
grub_relocator_backward_size = RELOCATOR_SIZEOF (_backward);
+#if defined(__x86_64__) && defined(GRUB_MACHINE_EFI)
+ compute_preamble_size ();
+#endif
}
void
diff --git a/grub-core/lib/ieee1275/tcg2.c b/grub-core/lib/ieee1275/tcg2.c
index 40161c2f9..28e7db225 100644
--- a/grub-core/lib/ieee1275/tcg2.c
+++ b/grub-core/lib/ieee1275/tcg2.c
@@ -56,6 +56,52 @@ grub_ieee1275_tpm_init (void)
return GRUB_ERR_NONE;
}
+grub_err_t
+grub_ieee1275_ibmvtpm_2hash_ext_log (grub_uint8_t pcrindex,
+ grub_uint32_t eventtype,
+ const char *description,
+ grub_size_t description_size,
+ void *buf, grub_size_t size)
+{
+ struct tpm_2hash_ext_log
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t method;
+ grub_ieee1275_cell_t ihandle;
+ grub_ieee1275_cell_t size;
+ grub_ieee1275_cell_t buf;
+ grub_ieee1275_cell_t description_size;
+ grub_ieee1275_cell_t description;
+ grub_ieee1275_cell_t eventtype;
+ grub_ieee1275_cell_t pcrindex;
+ grub_ieee1275_cell_t catch_result;
+ grub_ieee1275_cell_t rc;
+ };
+ struct tpm_2hash_ext_log args;
+
+ INIT_IEEE1275_COMMON (&args.common, "call-method", 8, 2);
+ args.method = (grub_ieee1275_cell_t) "2hash-ext-log";
+ args.ihandle = grub_ieee1275_tpm_ihandle;
+ args.pcrindex = pcrindex;
+ args.eventtype = eventtype;
+ args.description = (grub_ieee1275_cell_t) description;
+ args.description_size = description_size;
+ args.buf = (grub_ieee1275_cell_t) buf;
+ args.size = (grub_ieee1275_cell_t) size;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return grub_error (GRUB_ERR_BAD_DEVICE, "2HASH-EXT-LOG failed: Firmware is likely too old.\n");
+
+ /*
+ * catch_result is set if firmware does not support 2hash-ext-log
+ * rc is GRUB_IEEE1275_CELL_FALSE (0) on failure
+ */
+ if ((args.catch_result) || args.rc == GRUB_IEEE1275_CELL_FALSE)
+ return grub_error (GRUB_ERR_BAD_DEVICE, "2HASH-EXT-LOG failed: Firmware is likely too old.\n");
+
+ return GRUB_ERR_NONE;
+}
+
grub_err_t
grub_tcg2_get_max_output_size (grub_size_t *size)
{
@@ -155,3 +201,22 @@ grub_tcg2_submit_command (grub_size_t input_size,
return GRUB_ERR_NONE;
}
+
+grub_err_t
+grub_tcg2_cap_pcr (grub_uint8_t pcr)
+{
+ char separator[4] = {0};
+ static int error_displayed = 0;
+ grub_err_t err;
+
+ err = grub_ieee1275_ibmvtpm_2hash_ext_log (pcr, GRUB_EV_SEPARATOR,
+ separator, sizeof (separator),
+ separator, sizeof (separator));
+ if (err != GRUB_ERR_NONE && !error_displayed)
+ {
+ error_displayed++;
+ return err;
+ }
+
+ return GRUB_ERR_NONE;
+}
diff --git a/grub-core/lib/legacy_parse.c b/grub-core/lib/legacy_parse.c
index fa0131a1e..899530944 100644
--- a/grub-core/lib/legacy_parse.c
+++ b/grub-core/lib/legacy_parse.c
@@ -508,6 +508,9 @@ grub_legacy_parse (const char *buf, char **entryname, char **suffix)
char *ret;
int len = grub_strlen (buf);
ret = grub_malloc (len + 2);
+ if (ret == NULL)
+ return NULL;
+
grub_memcpy (ret, buf, len);
if (len && ret[len - 1] == '\n')
ret[len] = 0;
diff --git a/grub-core/lib/libgcrypt-patches/09-blake2b-hash-buffers.patch b/grub-core/lib/libgcrypt-patches/09-blake2b-hash-buffers.patch
new file mode 100644
index 000000000..fd4bae571
--- /dev/null
+++ b/grub-core/lib/libgcrypt-patches/09-blake2b-hash-buffers.patch
@@ -0,0 +1,63 @@
+From 42e9975171439e2e9713e122cb0e74174f057e98 Mon Sep 17 00:00:00 2001
+From: Gary Lin
+Date: Mon, 25 Aug 2025 15:54:24 +0800
+Subject: [PATCH 1/4] libgcrypt/kdf: Implement blake2b_512.hash_buffers()
+
+Add argon2_blake2b_512_hash_buffers() as the replacement of
+_gcry_digest_spec_blake2b_512.hash_buffers().
+
+Signed-off-by: Gary Lin
+---
+ grub-core/lib/libgcrypt-grub/cipher/kdf.c | 25 ++++++++++++++++++++++-
+ 1 file changed, 24 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/lib/libgcrypt-grub/cipher/kdf.c b/grub-core/lib/libgcrypt-grub/cipher/kdf.c
+index 0689f88b1..08e3ef658 100644
+--- a/grub-core/lib/libgcrypt-grub/cipher/kdf.c
++++ b/grub-core/lib/libgcrypt-grub/cipher/kdf.c
+@@ -129,10 +129,31 @@ beswap64_block (u64 *dst)
+ #endif
+ }
+
++/* Implementation of _gcry_blake2b_512_hash_buffers */
++static gcry_err_code_t
++argon2_blake2b_512_hash_buffers (void *outbuf, const gcry_buffer_t *iov, int iovcnt)
++{
++ void *hd;
++
++ hd = xtrymalloc (_gcry_digest_spec_blake2b_512.contextsize);
++ if (!hd)
++ return GPG_ERR_OUT_OF_MEMORY;
++
++ _gcry_digest_spec_blake2b_512.init (hd, 0);
++ for (;iovcnt > 0; iov++, iovcnt--)
++ _gcry_digest_spec_blake2b_512.write (hd, (const char*)iov[0].data + iov[0].off, iov[0].len);
++ _gcry_digest_spec_blake2b_512.final (hd);
++ grub_memcpy (outbuf, _gcry_digest_spec_blake2b_512.read (hd), 512 / 8);
++
++ xfree (hd);
++
++ return GPG_ERR_NO_ERROR;
++}
+
+ static gpg_err_code_t
+ argon2_fill_first_blocks (argon2_ctx_t a)
+ {
++ gpg_err_code_t err;
+ unsigned char h0_01_i[72];
+ unsigned char buf[10][4];
+ gcry_buffer_t iov[8];
+@@ -195,7 +216,9 @@ argon2_fill_first_blocks (argon2_ctx_t a)
+ iov_count++;
+ }
+
+- _gcry_digest_spec_blake2b_512.hash_buffers (h0_01_i, 64, iov, iov_count);
++ err = argon2_blake2b_512_hash_buffers (h0_01_i, iov, iov_count);
++ if (err != GPG_ERR_NO_ERROR)
++ return err;
+
+ for (i = 0; i < a->lanes; i++)
+ {
+--
+2.51.0
+
diff --git a/grub-core/lib/libgcrypt-patches/10-kdf-use-GPG-errs.patch b/grub-core/lib/libgcrypt-patches/10-kdf-use-GPG-errs.patch
new file mode 100644
index 000000000..53ca52d53
--- /dev/null
+++ b/grub-core/lib/libgcrypt-patches/10-kdf-use-GPG-errs.patch
@@ -0,0 +1,48 @@
+From 89f793515d927d8f7099b61d0b7b200611e56acd Mon Sep 17 00:00:00 2001
+From: Gary Lin
+Date: Mon, 25 Aug 2025 15:56:03 +0800
+Subject: [PATCH 2/4] libgcrypt/kdf: Get rid of gpg_err_code_from_errno()
+
+gpg_err_code_from_errno() requires libgcrypt_wrap/mem.c which is not in
+Makefile.utilgcry.def. This commit replaces gpg_err_code_from_errno()
+with GPG_ERR_* to avoid the build errors.
+
+Signed-off-by: Gary Lin
+---
+ grub-core/lib/libgcrypt-grub/cipher/kdf.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/lib/libgcrypt-grub/cipher/kdf.c b/grub-core/lib/libgcrypt-grub/cipher/kdf.c
+index 08e3ef658..6e0351d8a 100644
+--- a/grub-core/lib/libgcrypt-grub/cipher/kdf.c
++++ b/grub-core/lib/libgcrypt-grub/cipher/kdf.c
+@@ -265,7 +265,7 @@ argon2_init (argon2_ctx_t a, unsigned int parallelism,
+ block = xtrymalloc (1024 * memory_blocks);
+ if (!block)
+ {
+- ec = gpg_err_code_from_errno (errno);
++ ec = GPG_ERR_OUT_OF_MEMORY;
+ return ec;
+ }
+ memset (block, 0, 1024 * memory_blocks);
+@@ -273,7 +273,7 @@ argon2_init (argon2_ctx_t a, unsigned int parallelism,
+ thread_data = xtrymalloc (a->lanes * sizeof (struct argon2_thread_data));
+ if (!thread_data)
+ {
+- ec = gpg_err_code_from_errno (errno);
++ ec = GPG_ERR_OUT_OF_MEMORY;
+ xfree (block);
+ return ec;
+ }
+@@ -624,7 +624,7 @@ argon2_open (gcry_kdf_hd_t *hd, int subalgo,
+ n = offsetof (struct argon2_context, out) + taglen;
+ a = xtrymalloc (n);
+ if (!a)
+- return gpg_err_code_from_errno (errno);
++ return GPG_ERR_OUT_OF_MEMORY;
+
+ a->algo = GCRY_KDF_ARGON2;
+ a->hash_type = hash_type;
+--
+2.51.0
+
diff --git a/grub-core/lib/libgcrypt-patches/11-kdf-remove-unsupported-kdfs.patch b/grub-core/lib/libgcrypt-patches/11-kdf-remove-unsupported-kdfs.patch
new file mode 100644
index 000000000..44ef3dec2
--- /dev/null
+++ b/grub-core/lib/libgcrypt-patches/11-kdf-remove-unsupported-kdfs.patch
@@ -0,0 +1,166 @@
+From fc9c57f54fd28685f7df79e53078e1dc9e44f964 Mon Sep 17 00:00:00 2001
+From: Gary Lin
+Date: Mon, 25 Aug 2025 16:00:17 +0800
+Subject: [PATCH 3/4] libgcrypt/kdf: Remove unsupported KDFs
+
+Clean up _gcry_kdf_*() to remove unsupported KDFs.
+
+Signed-off-by: Gary Lin
+---
+ grub-core/lib/libgcrypt-grub/cipher/kdf.c | 119 ----------------------
+ 1 file changed, 119 deletions(-)
+
+diff --git a/grub-core/lib/libgcrypt-grub/cipher/kdf.c b/grub-core/lib/libgcrypt-grub/cipher/kdf.c
+index 6e0351d8a..c51a70eff 100644
+--- a/grub-core/lib/libgcrypt-grub/cipher/kdf.c
++++ b/grub-core/lib/libgcrypt-grub/cipher/kdf.c
+@@ -821,64 +821,6 @@ _gcry_kdf_open (gcry_kdf_hd_t *hd, int algo, int subalgo,
+ key, keylen, ad, adlen);
+ break;
+
+- case GCRY_KDF_BALLOON:
+- if (!inputlen || !saltlen || keylen || adlen)
+- ec = GPG_ERR_INV_VALUE;
+- else
+- {
+- (void)key;
+- (void)ad;
+- ec = balloon_open (hd, subalgo, param, paramlen,
+- input, inputlen, salt, saltlen);
+- }
+- break;
+-
+- case GCRY_KDF_ONESTEP_KDF:
+- if (!inputlen || !paramlen || !adlen)
+- ec = GPG_ERR_INV_VALUE;
+- else
+- {
+- (void)salt;
+- (void)key;
+- ec = onestep_kdf_open (hd, subalgo, param, paramlen,
+- input, inputlen, ad, adlen);
+- }
+- break;
+-
+- case GCRY_KDF_ONESTEP_KDF_MAC:
+- if (!inputlen || !paramlen || !keylen || !adlen)
+- ec = GPG_ERR_INV_VALUE;
+- else
+- {
+- (void)salt;
+- ec = onestep_kdf_mac_open (hd, subalgo, param, paramlen,
+- input, inputlen, key, keylen, ad, adlen);
+- }
+- break;
+-
+- case GCRY_KDF_HKDF:
+- if (!inputlen || !paramlen)
+- ec = GPG_ERR_INV_VALUE;
+- else
+- {
+- (void)salt;
+- ec = hkdf_open (hd, subalgo, param, paramlen,
+- input, inputlen, key, keylen, ad, adlen);
+- }
+- break;
+-
+- case GCRY_KDF_X963_KDF:
+- if (!inputlen || !paramlen)
+- ec = GPG_ERR_INV_VALUE;
+- else
+- {
+- (void)salt;
+- (void)key;
+- ec = x963_kdf_open (hd, subalgo, param, paramlen,
+- input, inputlen, ad, adlen);
+- }
+- break;
+-
+ default:
+ ec = GPG_ERR_UNKNOWN_ALGORITHM;
+ break;
+@@ -898,26 +840,6 @@ _gcry_kdf_compute (gcry_kdf_hd_t h, const struct gcry_kdf_thread_ops *ops)
+ ec = argon2_compute ((argon2_ctx_t)(void *)h, ops);
+ break;
+
+- case GCRY_KDF_BALLOON:
+- ec = balloon_compute_all ((balloon_ctx_t)(void *)h, ops);
+- break;
+-
+- case GCRY_KDF_ONESTEP_KDF:
+- ec = onestep_kdf_compute ((onestep_kdf_ctx_t)(void *)h, ops);
+- break;
+-
+- case GCRY_KDF_ONESTEP_KDF_MAC:
+- ec = onestep_kdf_mac_compute ((onestep_kdf_mac_ctx_t)(void *)h, ops);
+- break;
+-
+- case GCRY_KDF_HKDF:
+- ec = hkdf_compute ((hkdf_ctx_t)(void *)h, ops);
+- break;
+-
+- case GCRY_KDF_X963_KDF:
+- ec = x963_kdf_compute ((x963_kdf_ctx_t)(void *)h, ops);
+- break;
+-
+ default:
+ ec = GPG_ERR_UNKNOWN_ALGORITHM;
+ break;
+@@ -938,27 +860,6 @@ _gcry_kdf_final (gcry_kdf_hd_t h, size_t resultlen, void *result)
+ ec = argon2_final ((argon2_ctx_t)(void *)h, resultlen, result);
+ break;
+
+- case GCRY_KDF_BALLOON:
+- ec = balloon_final ((balloon_ctx_t)(void *)h, resultlen, result);
+- break;
+-
+- case GCRY_KDF_ONESTEP_KDF:
+- ec = onestep_kdf_final ((onestep_kdf_ctx_t)(void *)h, resultlen, result);
+- break;
+-
+- case GCRY_KDF_ONESTEP_KDF_MAC:
+- ec = onestep_kdf_mac_final ((onestep_kdf_mac_ctx_t)(void *)h,
+- resultlen, result);
+- break;
+-
+- case GCRY_KDF_HKDF:
+- ec = hkdf_final ((hkdf_ctx_t)(void *)h, resultlen, result);
+- break;
+-
+- case GCRY_KDF_X963_KDF:
+- ec = x963_kdf_final ((x963_kdf_ctx_t)(void *)h, resultlen, result);
+- break;
+-
+ default:
+ ec = GPG_ERR_UNKNOWN_ALGORITHM;
+ break;
+@@ -976,26 +877,6 @@ _gcry_kdf_close (gcry_kdf_hd_t h)
+ argon2_close ((argon2_ctx_t)(void *)h);
+ break;
+
+- case GCRY_KDF_BALLOON:
+- balloon_close ((balloon_ctx_t)(void *)h);
+- break;
+-
+- case GCRY_KDF_ONESTEP_KDF:
+- onestep_kdf_close ((onestep_kdf_ctx_t)(void *)h);
+- break;
+-
+- case GCRY_KDF_ONESTEP_KDF_MAC:
+- onestep_kdf_mac_close ((onestep_kdf_mac_ctx_t)(void *)h);
+- break;
+-
+- case GCRY_KDF_HKDF:
+- hkdf_close ((hkdf_ctx_t)(void *)h);
+- break;
+-
+- case GCRY_KDF_X963_KDF:
+- x963_kdf_close ((x963_kdf_ctx_t)(void *)h);
+- break;
+-
+ default:
+ break;
+ }
+--
+2.51.0
+
diff --git a/grub-core/lib/libgcrypt-patches/12-kdf-use-grub_divmod64.patch b/grub-core/lib/libgcrypt-patches/12-kdf-use-grub_divmod64.patch
new file mode 100644
index 000000000..644485ebf
--- /dev/null
+++ b/grub-core/lib/libgcrypt-patches/12-kdf-use-grub_divmod64.patch
@@ -0,0 +1,79 @@
+From 990a5f7df076200aa031b1bcde6bc4b13d4f198e Mon Sep 17 00:00:00 2001
+From: Gary Lin
+Date: Mon, 25 Aug 2025 16:01:45 +0800
+Subject: [PATCH 4/4] libgcrypt/kdf: Fix 64-bit modulus on 32-bit platforms
+
+Use grub_divmod64() for the 64-bit modulus to prevent creation of
+special division calls such as __umoddi3() and __aeabi_uldivmod() on
+32-bit platforms.
+
+Signed-off-by: Gary Lin
+---
+ grub-core/lib/libgcrypt-grub/cipher/kdf.c | 16 ++++++++++++----
+ 1 file changed, 12 insertions(+), 4 deletions(-)
+
+diff --git a/grub-core/lib/libgcrypt-grub/cipher/kdf.c b/grub-core/lib/libgcrypt-grub/cipher/kdf.c
+index c51a70eff..f4bb51809 100644
+--- a/grub-core/lib/libgcrypt-grub/cipher/kdf.c
++++ b/grub-core/lib/libgcrypt-grub/cipher/kdf.c
+@@ -375,6 +375,7 @@ index_alpha (argon2_ctx_t a, const struct argon2_thread_data *t,
+ u32 reference_area_size;
+ u64 relative_position;
+ u32 start_position;
++ u64 remainder;
+
+ if (t->pass == 0)
+ {
+@@ -411,7 +412,8 @@ index_alpha (argon2_ctx_t a, const struct argon2_thread_data *t,
+ ? 0
+ : (t->slice + 1) * a->segment_length;
+
+- return (start_position + relative_position) % a->lane_length;
++ grub_divmod64 (start_position + relative_position, a->lane_length, &remainder);
++ return remainder;
+ }
+
+ static void
+@@ -425,6 +427,7 @@ argon2_compute_segment (void *priv)
+ u64 input_block[1024/sizeof (u64)];
+ u64 address_block[1024/sizeof (u64)];
+ u64 *random_block = NULL;
++ u64 remainder;
+
+ if (a->hash_type == GCRY_KDF_ARGON2I
+ || (a->hash_type == GCRY_KDF_ARGON2ID && t->pass == 0 && t->slice < 2))
+@@ -449,7 +452,8 @@ argon2_compute_segment (void *priv)
+ i = 0;
+
+ curr_offset = t->lane * a->lane_length + t->slice * a->segment_length + i;
+- if ((curr_offset % a->lane_length))
++ grub_divmod64 (curr_offset, a->lane_length, &remainder);
++ if (remainder)
+ prev_offset = curr_offset - 1;
+ else
+ prev_offset = curr_offset + a->lane_length - 1;
+@@ -459,7 +463,8 @@ argon2_compute_segment (void *priv)
+ u64 *ref_block, *curr_block;
+ u64 rand64;
+
+- if ((curr_offset % a->lane_length) == 1)
++ grub_divmod64 (curr_offset, a->lane_length, &remainder);
++ if (remainder == 1)
+ prev_offset = curr_offset - 1;
+
+ if (random_block)
+@@ -475,7 +480,10 @@ argon2_compute_segment (void *priv)
+ if (t->pass == 0 && t->slice == 0)
+ ref_lane = t->lane;
+ else
+- ref_lane = (rand64 >> 32) % a->lanes;
++ {
++ grub_divmod64 (rand64 >> 32, a->lanes, &remainder);
++ ref_lane = remainder;
++ }
+
+ ref_index = index_alpha (a, t, i, (rand64 & 0xffffffff),
+ ref_lane == t->lane);
+--
+2.51.0
+
diff --git a/grub-core/lib/libgcrypt-patches/13_add_hwfeatures.patch b/grub-core/lib/libgcrypt-patches/13_add_hwfeatures.patch
new file mode 100644
index 000000000..1360b666e
--- /dev/null
+++ b/grub-core/lib/libgcrypt-patches/13_add_hwfeatures.patch
@@ -0,0 +1,87 @@
+From 4403c452240417aaac7d3120c80b1325b7218768 Mon Sep 17 00:00:00 2001
+From: Gary Lin
+Date: Fri, 18 Jul 2025 15:21:51 +0800
+Subject: [PATCH 1/4] libgcrypt: Implement _gcry_get_hw_features()
+
+Implement _gcry_get_hw_features() and enable hardware feature detection
+for x86_64.
+
+Signed-off-by: Gary Lin
+---
+ grub-core/Makefile.gcry.def | 8 ++++
+ grub-core/lib/libgcrypt-grub/src/hwfeatures.c | 47 +++++++++++++++++++
+ 2 files changed, 55 insertions(+)
+ create mode 100644 grub-core/lib/libgcrypt-grub/src/hwfeatures.c
+
+diff --git a/grub-core/Makefile.gcry.def b/grub-core/Makefile.gcry.def
+index a0593fa09..c8caf17dc 100644
+--- a/grub-core/Makefile.gcry.def
++++ b/grub-core/Makefile.gcry.def
+@@ -226,3 +226,11 @@ module = {
+ cppflags = '$(CPPFLAGS_GCRY)';
+ };
+
++module = {
++ name = gcry_hwfeatures;
++ common = lib/libgcrypt-grub/src/hwfeatures.c;
++ x86_64_efi = lib/libgcrypt-grub/src/hwf-x86.c;
++
++ cflags = '$(CFLAGS_GCRY)';
++ cppflags = '$(CPPFLAGS_GCRY) $(CPPFLAGS_GCRY_ASM)';
++};
+diff --git a/grub-core/lib/libgcrypt-grub/src/hwfeatures.c b/grub-core/lib/libgcrypt-grub/src/hwfeatures.c
+new file mode 100644
+index 000000000..4d744f8ec
+--- /dev/null
++++ b/grub-core/lib/libgcrypt-grub/src/hwfeatures.c
+@@ -0,0 +1,47 @@
++/*
++ * GRUB -- GRand Unified Bootloader
++ * Copyright (C) 2025 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 .
++ */
++
++#include
++GRUB_MOD_LICENSE ("GPLv3+");
++
++#include
++#include
++#include "hwf-common.h"
++
++unsigned int
++_gcry_get_hw_features (void)
++{
++ static bool detected = false;
++ static unsigned int hw_features = 0;
++
++ if (grub_gcry_hwf_enabled () == false)
++ return 0;
++
++ if (detected == true)
++ return hw_features;
++
++#if defined (__x86_64__) && defined (GRUB_MACHINE_EFI)
++ hw_features = _gcry_hwf_detect_x86 ();
++#endif
++
++ grub_dprintf ("hwfeatures", "Detected features: 0x%x\n", hw_features);
++
++ detected = true;
++
++ return hw_features;
++}
+--
+2.51.0
+
diff --git a/grub-core/lib/libgcrypt-patches/14_fix_build_shaext.patch b/grub-core/lib/libgcrypt-patches/14_fix_build_shaext.patch
new file mode 100644
index 000000000..b9d311465
--- /dev/null
+++ b/grub-core/lib/libgcrypt-patches/14_fix_build_shaext.patch
@@ -0,0 +1,35 @@
+From 5698f7c5055ea481d0040ea4495829e5d02781cc Mon Sep 17 00:00:00 2001
+From: Gary Lin
+Date: Fri, 18 Jul 2025 15:34:21 +0800
+Subject: [PATCH 2/4] libgcrypt: Declare the sha256 shaext function
+
+There is no prototype of _gcry_sha256_transform_intel_shaext() defined
+in the header or libgcrypt-grub/cipher/sha256.c, and gcc may complain
+the missing-prototypes error when compiling sha256-intel-shaext.c.
+
+Declare the prototype in sha256-intel-shaext.c to avoid the error.
+
+Signed-off-by: Gary Lin
+---
+ grub-core/lib/libgcrypt-grub/cipher/sha256-intel-shaext.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/grub-core/lib/libgcrypt-grub/cipher/sha256-intel-shaext.c b/grub-core/lib/libgcrypt-grub/cipher/sha256-intel-shaext.c
+index 48c09eefe..7ec49f05e 100644
+--- a/grub-core/lib/libgcrypt-grub/cipher/sha256-intel-shaext.c
++++ b/grub-core/lib/libgcrypt-grub/cipher/sha256-intel-shaext.c
+@@ -95,6 +95,11 @@ typedef struct u128_s
+ u32 a, b, c, d;
+ } u128_t;
+
++
++unsigned int ASM_FUNC_ATTR
++_gcry_sha256_transform_intel_shaext(u32 state[8], const unsigned char *data,
++ size_t nblks);
++
+ /*
+ * Transform nblks*64 bytes (nblks*16 32-bit words) at DATA.
+ */
+--
+2.51.0
+
diff --git a/grub-core/lib/libgcrypt-patches/15_build_sha256_x86_64_efi_opt_code.patch b/grub-core/lib/libgcrypt-patches/15_build_sha256_x86_64_efi_opt_code.patch
new file mode 100644
index 000000000..8cab9b866
--- /dev/null
+++ b/grub-core/lib/libgcrypt-patches/15_build_sha256_x86_64_efi_opt_code.patch
@@ -0,0 +1,43 @@
+From 3443b213bb87112144d753678d0f1bbbc72b2b7a Mon Sep 17 00:00:00 2001
+From: Gary Lin
+Date: Fri, 18 Jul 2025 15:23:25 +0800
+Subject: [PATCH 3/4] libgcrypt: Add hardware acceleration for gcry_sha256
+
+Enable hardware acceleration for the gcry_sha256 module when building
+for the x86_64 EFI target.
+
+Signed-off-by: Gary Lin
+---
+ grub-core/Makefile.gcry.def | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/Makefile.gcry.def b/grub-core/Makefile.gcry.def
+index c8caf17dc..ac1d9a088 100644
+--- a/grub-core/Makefile.gcry.def
++++ b/grub-core/Makefile.gcry.def
+@@ -17,6 +17,7 @@ module = {
+ module = {
+ name = gcry_blake2;
+ common = lib/libgcrypt-grub/cipher/blake2.c;
++
+ cflags = '$(CFLAGS_GCRY)';
+ cppflags = '$(CPPFLAGS_GCRY)';
+ };
+@@ -172,8 +173,13 @@ module = {
+ module = {
+ name = gcry_sha256;
+ common = lib/libgcrypt-grub/cipher/sha256.c;
++ x86_64_efi = lib/libgcrypt-grub/cipher/sha256-ssse3-amd64.S;
++ x86_64_efi = lib/libgcrypt-grub/cipher/sha256-avx-amd64.S;
++ x86_64_efi = lib/libgcrypt-grub/cipher/sha256-avx2-bmi2-amd64.S;
++ x86_64_efi = lib/libgcrypt-grub/cipher/sha256-intel-shaext.c;
++
+ cflags = '$(CFLAGS_GCRY) -Wno-cast-align';
+- cppflags = '$(CPPFLAGS_GCRY)';
++ cppflags = '$(CPPFLAGS_GCRY) -DUSE_SHA256 $(CPPFLAGS_GCRY_ASM)';
+ };
+
+ module = {
+--
+2.51.0
+
diff --git a/grub-core/lib/libgcrypt-patches/16_build_sha512_x86_64_efi_opt_code.patch b/grub-core/lib/libgcrypt-patches/16_build_sha512_x86_64_efi_opt_code.patch
new file mode 100644
index 000000000..9cdde8830
--- /dev/null
+++ b/grub-core/lib/libgcrypt-patches/16_build_sha512_x86_64_efi_opt_code.patch
@@ -0,0 +1,35 @@
+From f62c2c7565237bbf059220e90d3f1f8c8d6eebd5 Mon Sep 17 00:00:00 2001
+From: Gary Lin
+Date: Wed, 3 Sep 2025 16:26:55 +0800
+Subject: [PATCH 4/4] libgcrypt: Add hardware acceleration for gcry_sha512
+
+Enable hardware acceleration for the gcry_sha512 module when building
+for the x86_64 EFI target.
+
+Signed-off-by: Gary Lin
+---
+ grub-core/Makefile.gcry.def | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/grub-core/Makefile.gcry.def b/grub-core/Makefile.gcry.def
+index ac1d9a088..64ea5ee24 100644
+--- a/grub-core/Makefile.gcry.def
++++ b/grub-core/Makefile.gcry.def
+@@ -186,8 +186,13 @@ module = {
+ name = gcry_sha512;
+ common = lib/libgcrypt-grub/cipher/sha512.c;
+ common = lib/libgcrypt-grub/cipher/hash-common.c;
++ x86_64_efi = lib/libgcrypt-grub/cipher/sha512-ssse3-amd64.S;
++ x86_64_efi = lib/libgcrypt-grub/cipher/sha512-avx-amd64.S;
++ x86_64_efi = lib/libgcrypt-grub/cipher/sha512-avx2-bmi2-amd64.S;
++ x86_64_efi = lib/libgcrypt-grub/cipher/sha512-avx512-amd64.S;
++
+ cflags = '$(CFLAGS_GCRY) -Wno-cast-align';
+- cppflags = '$(CPPFLAGS_GCRY)';
++ cppflags = '$(CPPFLAGS_GCRY) -DUSE_SHA512 $(CPPFLAGS_GCRY_ASM)';
+ };
+
+ module = {
+--
+2.51.0
+
diff --git a/grub-core/lib/mips/relocator.c b/grub-core/lib/mips/relocator.c
index 773f3b769..9f79b40c8 100644
--- a/grub-core/lib/mips/relocator.c
+++ b/grub-core/lib/mips/relocator.c
@@ -45,6 +45,7 @@ grub_size_t grub_relocator_align = sizeof (grub_uint32_t);
grub_size_t grub_relocator_forward_size;
grub_size_t grub_relocator_backward_size;
grub_size_t grub_relocator_jumper_size = JUMP_SIZEOF + REGW_SIZEOF;
+grub_size_t grub_relocator_preamble_size = 0;
void
grub_cpu_relocator_init (void)
@@ -53,6 +54,11 @@ grub_cpu_relocator_init (void)
grub_relocator_backward_size = RELOCATOR_SIZEOF(backward);
}
+void
+grub_cpu_relocator_preamble (void *rels __attribute__ ((unused)))
+{
+}
+
static void
write_reg (int regn, grub_uint32_t val, void **target)
{
diff --git a/grub-core/lib/pbkdf2.c b/grub-core/lib/pbkdf2.c
index 28aa96c46..410eff580 100644
--- a/grub-core/lib/pbkdf2.c
+++ b/grub-core/lib/pbkdf2.c
@@ -39,6 +39,7 @@ grub_crypto_pbkdf2 (const struct gcry_md_spec *md,
unsigned int c,
grub_uint8_t *DK, grub_size_t dkLen)
{
+ struct grub_crypto_hmac_handle *hnd = NULL;
unsigned int hLen = md->mdlen;
grub_uint8_t U[GRUB_CRYPTO_MAX_MDLEN];
grub_uint8_t T[GRUB_CRYPTO_MAX_MDLEN];
@@ -47,7 +48,6 @@ grub_crypto_pbkdf2 (const struct gcry_md_spec *md,
unsigned int r;
unsigned int i;
unsigned int k;
- gcry_err_code_t rc;
grub_uint8_t *tmp;
grub_size_t tmplen = Slen + 4;
@@ -72,6 +72,13 @@ grub_crypto_pbkdf2 (const struct gcry_md_spec *md,
grub_memcpy (tmp, S, Slen);
+ hnd = grub_crypto_hmac_init (md, P, Plen);
+ if (hnd == NULL)
+ {
+ grub_free (tmp);
+ return GPG_ERR_OUT_OF_MEMORY;
+ }
+
for (i = 1; i - 1 < l; i++)
{
grub_memset (T, 0, hLen);
@@ -85,16 +92,13 @@ grub_crypto_pbkdf2 (const struct gcry_md_spec *md,
tmp[Slen + 2] = (i & 0x0000ff00) >> 8;
tmp[Slen + 3] = (i & 0x000000ff) >> 0;
- rc = grub_crypto_hmac_buffer (md, P, Plen, tmp, tmplen, U);
+ grub_crypto_hmac_write (hnd, tmp, tmplen);
}
else
- rc = grub_crypto_hmac_buffer (md, P, Plen, U, hLen, U);
+ grub_crypto_hmac_write (hnd, U, hLen);
- if (rc != GPG_ERR_NO_ERROR)
- {
- grub_free (tmp);
- return rc;
- }
+ grub_crypto_hmac_final (hnd, U);
+ grub_crypto_hmac_reset (hnd);
for (k = 0; k < hLen; k++)
T[k] ^= U[k];
@@ -103,6 +107,7 @@ grub_crypto_pbkdf2 (const struct gcry_md_spec *md,
grub_memcpy (DK + (i - 1) * hLen, T, i == l ? r : hLen);
}
+ grub_crypto_hmac_free (hnd);
grub_free (tmp);
return GPG_ERR_NO_ERROR;
diff --git a/grub-core/lib/powerpc/relocator.c b/grub-core/lib/powerpc/relocator.c
index 15aeb0246..559e04b5a 100644
--- a/grub-core/lib/powerpc/relocator.c
+++ b/grub-core/lib/powerpc/relocator.c
@@ -43,6 +43,7 @@ grub_size_t grub_relocator_align = sizeof (grub_uint32_t);
grub_size_t grub_relocator_forward_size;
grub_size_t grub_relocator_backward_size;
grub_size_t grub_relocator_jumper_size = JUMP_SIZEOF + REGW_SIZEOF;
+grub_size_t grub_relocator_preamble_size = 0;
void
grub_cpu_relocator_init (void)
@@ -51,6 +52,11 @@ grub_cpu_relocator_init (void)
grub_relocator_backward_size = RELOCATOR_SIZEOF(backward);
}
+void
+grub_cpu_relocator_preamble (void *rels __attribute__((unused)))
+{
+}
+
static void
write_reg (int regn, grub_uint32_t val, void **target)
{
diff --git a/grub-core/lib/relocator.c b/grub-core/lib/relocator.c
index 3306a1bb7..37da0c6db 100644
--- a/grub-core/lib/relocator.c
+++ b/grub-core/lib/relocator.c
@@ -110,7 +110,7 @@ grub_relocator_new (void)
return NULL;
ret->postchunks = ~(grub_phys_addr_t) 0;
- ret->relocators_size = grub_relocator_jumper_size;
+ ret->relocators_size = grub_relocator_jumper_size + grub_relocator_preamble_size;
grub_dprintf ("relocator", "relocators_size=%lu\n",
(unsigned long) ret->relocators_size);
return ret;
@@ -398,9 +398,9 @@ free_subchunk (const struct grub_relocator_subchunk *subchu)
if (subchu->post)
{
int off = subchu->start + subchu->size - fend;
- grub_memset (subchu->pre->freebytes,
- 0xff, sizeof (subchu->pre->freebytes) - off / 8);
- subchu->pre->freebytes[off / 8] |= ((1 << (8 - (off % 8))) - 1);
+ grub_memset (subchu->post->freebytes,
+ 0xff, sizeof (subchu->post->freebytes) - off / 8 - 1);
+ subchu->post->freebytes[sizeof (subchu->post->freebytes) - off / 8 - 1] |= ((1 << (8 - (off % 8))) - 1);
check_leftover (subchu->post);
}
#endif
@@ -1605,6 +1605,9 @@ grub_relocator_prepare_relocs (struct grub_relocator *rel, grub_addr_t addr,
grub_free (to);
}
+ grub_cpu_relocator_preamble (rels);
+ rels += grub_relocator_preamble_size;
+
for (j = 0; j < nchunks; j++)
{
grub_dprintf ("relocator", "sorted chunk %p->%p, 0x%lx\n",
diff --git a/grub-core/lib/tss2/tcg2.h b/grub-core/lib/tss2/tcg2.h
index 3d26373dd..ba3d11278 100644
--- a/grub-core/lib/tss2/tcg2.h
+++ b/grub-core/lib/tss2/tcg2.h
@@ -23,6 +23,8 @@
#include
#include
+#define GRUB_EV_SEPARATOR 0x04
+
extern grub_err_t
grub_tcg2_get_max_output_size (grub_size_t *size);
@@ -32,4 +34,7 @@ grub_tcg2_submit_command (grub_size_t input_size,
grub_size_t output_size,
grub_uint8_t *output);
+extern grub_err_t
+grub_tcg2_cap_pcr (grub_uint8_t pcr);
+
#endif /* ! GRUB_TPM2_TCG2_HEADER */
diff --git a/grub-core/lib/tss2/tcg2_emu.c b/grub-core/lib/tss2/tcg2_emu.c
index cab930d2b..3713190ac 100644
--- a/grub-core/lib/tss2/tcg2_emu.c
+++ b/grub-core/lib/tss2/tcg2_emu.c
@@ -22,6 +22,7 @@
#include
#include
+#include
#include
grub_err_t
@@ -47,3 +48,22 @@ grub_tcg2_submit_command (grub_size_t input_size, grub_uint8_t *input,
return GRUB_ERR_NONE;
}
+
+grub_err_t
+grub_tcg2_cap_pcr (grub_uint8_t pcr)
+{
+ TPMS_AUTH_COMMAND_t authCmd = {
+ .sessionHandle = TPM_RS_PW,
+ };
+ TPM2B_EVENT_t data = {
+ .size = 4,
+ };
+ TPM_RC_t rc;
+
+ /* Submit an EV_SEPARATOR event, i.e. an event with 4 zero-bytes */
+ rc = grub_tpm2_pcr_event (pcr, &authCmd, &data, NULL, NULL);
+ if (rc != TPM_RC_SUCCESS)
+ return grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot cap PCR %u"), pcr);
+
+ return GRUB_ERR_NONE;
+}
diff --git a/grub-core/lib/tss2/tpm2_cmd.c b/grub-core/lib/tss2/tpm2_cmd.c
index 6d25db1ab..c6996cac8 100644
--- a/grub-core/lib/tss2/tpm2_cmd.c
+++ b/grub-core/lib/tss2/tpm2_cmd.c
@@ -89,6 +89,7 @@ tpm2_submit_command (const TPMI_ST_COMMAND_TAG_t tag,
/* Catch TPM_RC_RETRY and send the command again */
do {
+ grub_tpm2_buffer_init (out);
err = tpm2_submit_command_real (tag, commandCode, responseCode, in, out);
if (*responseCode != TPM_RC_RETRY)
break;
@@ -167,7 +168,6 @@ grub_tpm2_createprimary (const TPMI_RH_HIERARCHY_t primaryHandle,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_CreatePrimary, &responseCode, &in, &out);
if (rc != TPM_RC_SUCCESS)
return rc;
@@ -250,7 +250,6 @@ grub_tpm2_startauthsession (const TPMI_DH_OBJECT_t tpmKey,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_StartAuthSession, &responseCode,
&in, &out);
if (rc != TPM_RC_SUCCESS)
@@ -308,7 +307,6 @@ grub_tpm2_policypcr (const TPMI_SH_POLICY_t policySessions,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_PolicyPCR, &responseCode, &in, &out);
if (rc != TPM_RC_SUCCESS)
return rc;
@@ -347,7 +345,6 @@ grub_tpm2_readpublic (const TPMI_DH_OBJECT_t objectHandle,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_ReadPublic, &responseCode, &in, &out);
if (rc != TPM_RC_SUCCESS)
return rc;
@@ -408,7 +405,6 @@ grub_tpm2_load (const TPMI_DH_OBJECT_t parent_handle,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_Load, &responseCode, &in, &out);
if (rc != TPM_RC_SUCCESS)
return rc;
@@ -475,7 +471,6 @@ grub_tpm2_loadexternal (const TPMS_AUTH_COMMAND_t *authCommand,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_LoadExternal, &responseCode, &in, &out);
if (rc != TPM_RC_SUCCESS)
return rc;
@@ -527,7 +522,6 @@ grub_tpm2_unseal (const TPMI_DH_OBJECT_t itemHandle,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_Unseal, &responseCode, &in, &out);
if (rc != TPM_RC_SUCCESS)
return rc;
@@ -561,7 +555,6 @@ grub_tpm2_flushcontext (const TPMI_DH_CONTEXT_t handle)
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (TPM_ST_NO_SESSIONS, TPM_CC_FlushContext, &responseCode, &in, &out);
if (rc != TPM_RC_SUCCESS)
return rc;
@@ -575,6 +568,56 @@ grub_tpm2_flushcontext (const TPMI_DH_CONTEXT_t handle)
return TPM_RC_SUCCESS;
}
+TPM_RC_t
+grub_tpm2_pcr_event (const TPMI_DH_PCR_t pcrHandle,
+ const TPMS_AUTH_COMMAND_t *authCommand,
+ const TPM2B_EVENT_t *eventData,
+ TPML_DIGEST_VALUES_t *digests,
+ TPMS_AUTH_RESPONSE_t *authResponse)
+{
+ TPM_RC_t rc;
+ struct grub_tpm2_buffer in;
+ struct grub_tpm2_buffer out;
+ TPML_DIGEST_VALUES_t digestsTmp;
+ TPMS_AUTH_RESPONSE_t authResponseTmp;
+ TPM_RC_t responseCode;
+ grub_uint32_t parameterSize;
+
+ if (eventData == NULL)
+ return TPM_RC_VALUE;
+ if (authCommand == NULL)
+ return TPM_RC_VALUE;
+
+ if (digests == NULL)
+ digests = &digestsTmp;
+ if (authResponse == NULL)
+ authResponse = &authResponseTmp;
+
+ /* Marshal */
+ grub_tpm2_buffer_init (&in);
+ grub_tpm2_buffer_pack_u32 (&in, pcrHandle);
+ grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand);
+ grub_Tss2_MU_TPM2B_Marshal (&in, eventData->size, eventData->buffer);
+ if (in.error == true)
+ return TPM_RC_FAILURE;
+
+ /* Submit */
+ rc = tpm2_submit_command (TPM_ST_SESSIONS, TPM_CC_PCR_Event, &responseCode, &in, &out);
+ if (rc != TPM_RC_SUCCESS)
+ return rc;
+ if (responseCode != TPM_RC_SUCCESS)
+ return responseCode;
+
+ /* Unmarshal */
+ grub_tpm2_buffer_unpack_u32 (&out, ¶meterSize);
+ grub_Tss2_MU_TPML_DIGEST_VALUE_Unmarshal (&out, digests);
+ grub_Tss2_MU_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse);
+ if (out.error == true)
+ return TPM_RC_FAILURE;
+
+ return TPM_RC_SUCCESS;
+}
+
TPM_RC_t
grub_tpm2_pcr_read (const TPMS_AUTH_COMMAND_t *authCommand,
const TPML_PCR_SELECTION_t *pcrSelectionIn,
@@ -615,7 +658,6 @@ grub_tpm2_pcr_read (const TPMS_AUTH_COMMAND_t *authCommand,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_PCR_Read, &responseCode, &in, &out);
if (rc != TPM_RC_SUCCESS)
return rc;
@@ -668,7 +710,6 @@ grub_tpm2_policygetdigest (const TPMI_SH_POLICY_t policySession,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_PolicyGetDigest, &responseCode, &in, &out);
if (rc != TPM_RC_SUCCESS)
return rc;
@@ -751,7 +792,6 @@ grub_tpm2_create (const TPMI_DH_OBJECT_t parentHandle,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_Create, &responseCode, &in, &out);
if (rc != TPM_RC_SUCCESS)
return rc;
@@ -805,7 +845,6 @@ grub_tpm2_evictcontrol (const TPMI_RH_PROVISION_t auth,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_EvictControl, &responseCode, &in, &out);
if (rc != TPM_RC_SUCCESS)
return rc;
@@ -871,7 +910,6 @@ grub_tpm2_hash (const TPMS_AUTH_COMMAND_t *authCommand,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_Hash, &responseCode, &in, &out);
if (rc != TPM_RC_SUCCESS)
return rc;
@@ -930,7 +968,6 @@ grub_tpm2_verifysignature (const TPMI_DH_OBJECT_t keyHandle,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_VerifySignature, &responseCode, &in, &out);
if (rc != TPM_RC_SUCCESS)
return rc;
@@ -990,7 +1027,6 @@ grub_tpm2_policyauthorize (const TPMI_SH_POLICY_t policySession,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_PolicyAuthorize, &responseCode, &in, &out);
if (rc != TPM_RC_SUCCESS)
return rc;
@@ -1031,7 +1067,6 @@ grub_tpm2_testparms (const TPMT_PUBLIC_PARMS_t *parms,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_TestParms, &responseCode, &in,
&out);
if (rc != TPM_RC_SUCCESS)
@@ -1075,7 +1110,6 @@ grub_tpm2_nv_definespace (const TPMI_RH_PROVISION_t authHandle,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_NV_DefineSpace, &responseCode, &in, &out);
if (rc != TPM_RC_SUCCESS)
return rc;
@@ -1110,7 +1144,6 @@ grub_tpm2_nv_undefinespace (const TPMI_RH_PROVISION_t authHandle,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_NV_UndefineSpace, &responseCode, &in, &out);
if (rc != TPM_RC_SUCCESS)
return rc;
@@ -1146,7 +1179,6 @@ grub_tpm2_nv_readpublic (const TPMI_RH_NV_INDEX_t nvIndex,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_NV_ReadPublic, &responseCode, &in, &out);
if (rc != TPM_RC_SUCCESS)
return rc;
@@ -1191,7 +1223,6 @@ grub_tpm2_nv_read (const TPMI_RH_NV_AUTH_t authHandle,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_NV_Read, &responseCode, &in, &out);
if (rc != TPM_RC_SUCCESS)
return rc;
@@ -1233,7 +1264,6 @@ grub_tpm2_nv_write (const TPMI_RH_NV_AUTH_t authHandle,
return TPM_RC_FAILURE;
/* Submit */
- grub_tpm2_buffer_init (&out);
rc = tpm2_submit_command (tag, TPM_CC_NV_Write, &responseCode, &in, &out);
if (rc != TPM_RC_SUCCESS)
return rc;
diff --git a/grub-core/lib/tss2/tpm2_cmd.h b/grub-core/lib/tss2/tpm2_cmd.h
index 90b42efec..d7ad962ab 100644
--- a/grub-core/lib/tss2/tpm2_cmd.h
+++ b/grub-core/lib/tss2/tpm2_cmd.h
@@ -89,6 +89,13 @@ grub_tpm2_unseal (const TPMI_DH_OBJECT_t item_handle,
extern TPM_RC_t
grub_tpm2_flushcontext (const TPMI_DH_CONTEXT_t handle);
+extern TPM_RC_t
+grub_tpm2_pcr_event (const TPMI_DH_PCR_t pcrHandle,
+ const TPMS_AUTH_COMMAND_t *authCommand,
+ const TPM2B_EVENT_t *eventData,
+ TPML_DIGEST_VALUES_t *digests,
+ TPMS_AUTH_RESPONSE_t *authResponse);
+
extern TPM_RC_t
grub_tpm2_pcr_read (const TPMS_AUTH_COMMAND_t *authCommand,
const TPML_PCR_SELECTION_t *pcrSelectionIn,
diff --git a/grub-core/lib/tss2/tss2_mu.c b/grub-core/lib/tss2/tss2_mu.c
index 816e5b37f..e544e62f9 100644
--- a/grub-core/lib/tss2/tss2_mu.c
+++ b/grub-core/lib/tss2/tss2_mu.c
@@ -1118,6 +1118,24 @@ grub_Tss2_MU_TPML_DIGEST_Unmarshal (grub_tpm2_buffer_t buffer,
grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (buffer, &digest->digests[i]);
}
+void
+grub_Tss2_MU_TPML_DIGEST_VALUE_Unmarshal (grub_tpm2_buffer_t buffer,
+ TPML_DIGEST_VALUES_t *digests)
+{
+ grub_uint32_t i;
+
+ grub_tpm2_buffer_unpack_u32 (buffer, &digests->count);
+
+ if (digests->count > TPM_NUM_PCR_BANKS)
+ {
+ buffer->error = true;
+ return;
+ }
+
+ for (i = 0; i < digests->count; i++)
+ grub_Tss2_MU_TPMT_HA_Unmarshal (buffer, &digests->digests[i]);
+}
+
void
grub_Tss2_MU_TPMS_SIGNATURE_RSA_Unmarshal (grub_tpm2_buffer_t buffer,
TPMS_SIGNATURE_RSA_t *rsa)
diff --git a/grub-core/lib/tss2/tss2_mu.h b/grub-core/lib/tss2/tss2_mu.h
index 6440de57c..76eebc994 100644
--- a/grub-core/lib/tss2/tss2_mu.h
+++ b/grub-core/lib/tss2/tss2_mu.h
@@ -380,6 +380,10 @@ extern void
grub_Tss2_MU_TPML_DIGEST_Unmarshal (grub_tpm2_buffer_t buffer,
TPML_DIGEST_t *digest);
+extern void
+grub_Tss2_MU_TPML_DIGEST_VALUE_Unmarshal (grub_tpm2_buffer_t buffer,
+ TPML_DIGEST_VALUES_t *digests);
+
extern void
grub_Tss2_MU_TPMS_SIGNATURE_RSA_Unmarshal (grub_tpm2_buffer_t buffer,
TPMS_SIGNATURE_RSA_t *p);
diff --git a/grub-core/lib/tss2/tss2_structs.h b/grub-core/lib/tss2/tss2_structs.h
index 2eefba87c..0ac09f50f 100644
--- a/grub-core/lib/tss2/tss2_structs.h
+++ b/grub-core/lib/tss2/tss2_structs.h
@@ -144,6 +144,13 @@ typedef struct TPML_DIGEST TPML_DIGEST_t;
/* TPM2B_NONCE Type */
typedef TPM2B_DIGEST_t TPM2B_NONCE_t;
+/* TPM2B_EVENT Structure */
+struct TPM2B_EVENT {
+ grub_uint16_t size;
+ grub_uint8_t buffer[1024];
+};
+typedef struct TPM2B_EVENT TPM2B_EVENT_t;
+
/* TPMA_SESSION Structure */
struct TPMA_SESSION
{
diff --git a/grub-core/lib/tss2/tss2_types.h b/grub-core/lib/tss2/tss2_types.h
index bddde7191..52d304b90 100644
--- a/grub-core/lib/tss2/tss2_types.h
+++ b/grub-core/lib/tss2/tss2_types.h
@@ -343,6 +343,7 @@ typedef grub_uint32_t TPM_CC_t;
#define TPM_CC_NV_Write ((TPM_CC_t) 0x00000137)
#define TPM_CC_NV_UndefineSpace ((TPM_CC_t) 0x00000122)
#define TPM_CC_GetCapability ((TPM_CC_t) 0x0000017a)
+#define TPM_CC_PCR_Event ((TPM_CC_t) 0x0000013c)
#define TPM_CC_PCR_Read ((TPM_CC_t) 0x0000017e)
#define TPM_CC_Load ((TPM_CC_t) 0x00000157)
#define TPM_CC_LoadExternal ((TPM_CC_t) 0x00000167)
diff --git a/grub-core/lib/x86_64/efi/hwfeatures-gcry.c b/grub-core/lib/x86_64/efi/hwfeatures-gcry.c
new file mode 100644
index 000000000..8e26c010d
--- /dev/null
+++ b/grub-core/lib/x86_64/efi/hwfeatures-gcry.c
@@ -0,0 +1,246 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2025 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 .
+ */
+
+#include
+#include
+#include
+#include
+
+/*
+ * Older versions of GCC may reorder the inline asm, which can lead to
+ * unexpected behavior when reading the Control Registers. The __FORCE_ORDER
+ * macro is used to prevent this.
+ *
+ * Ref: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=aa5cacdc29d76a005cbbee018a47faa6e724dd2d
+ */
+#define __FORCE_ORDER "m"(*(unsigned int *) 0x1000UL)
+
+#define HW_FEATURE_X86_64_SSE (1 << 0)
+#define HW_FEATURE_X86_64_AVX (1 << 1)
+
+static grub_uint32_t hw_features = 0;
+static grub_uint64_t old_cr0, old_cr4, old_xcr0;
+
+static grub_uint64_t
+read_cr0 (void)
+{
+ grub_uint64_t val;
+
+ asm volatile ("mov %%cr0, %0" : "=r" (val) : __FORCE_ORDER);
+ return val;
+}
+
+static grub_uint64_t
+read_cr4 (void)
+{
+ grub_uint64_t val;
+
+ asm volatile ("mov %%cr4,%0" : "=r" (val) : __FORCE_ORDER);
+ return val;
+}
+
+static void
+write_cr0 (grub_uint64_t val)
+{
+ asm volatile ("mov %0,%%cr4": "+r" (val) : : "memory");
+}
+
+static void
+write_cr4 (grub_uint64_t val)
+{
+ asm volatile ("mov %0,%%cr4": "+r" (val) : : "memory");
+}
+
+static grub_uint32_t
+get_cpuid_ecx (void)
+{
+ grub_uint32_t eax, ebx, ecx, edx;
+
+ grub_cpuid (1, eax, ebx, ecx, edx);
+
+ return ecx;
+}
+
+static grub_uint32_t
+get_cpuid_edx (void)
+{
+ grub_uint32_t eax, ebx, ecx, edx;
+
+ grub_cpuid (1, eax, ebx, ecx, edx);
+
+ return edx;
+}
+
+static bool
+enable_sse (void)
+{
+ grub_uint64_t cr0, cr4;
+ grub_uint32_t edx;
+
+ edx = get_cpuid_edx ();
+
+ /* Check CPUID.01H:EDX.FXSR[bit 24] and CPUID.01H:EDX.SSE[bit 25] */
+ if ((edx & (3 << 24)) != (3 << 24))
+ return false;
+
+ cr0 = old_cr0 = read_cr0 ();
+ cr4 = old_cr4 = read_cr4 ();
+
+ /* clear CR0.EM[bit 2] */
+ if ((cr0 & (1 << 2)) != 0)
+ cr0 &= ~(1 << 2);
+
+ /* Set CR0.MP[bit 1] */
+ if ((cr0 & (1 << 1)) == 0)
+ cr0 |= (1 << 1);
+
+ grub_dprintf ("hwfeatures", "CR0: 0x%"PRIxGRUB_UINT64_T" 0x%"PRIxGRUB_UINT64_T"\n", old_cr0, cr0);
+ if (old_cr0 != cr0)
+ write_cr0 (cr0);
+
+ /* Set CR4.OSFXSR[bit 9] and CR4.OSXMMEXCPT[bit 10] */
+ if ((cr4 & (3 << 9)) != (3 << 9))
+ cr4 |= (3 << 9);
+
+ grub_dprintf ("hwfeatures", "CR4: 0x%"PRIxGRUB_UINT64_T" 0x%"PRIxGRUB_UINT64_T"\n", old_cr4, cr4);
+ if (old_cr4 != cr4)
+ write_cr4 (cr4);
+
+ return true;
+}
+
+static grub_uint64_t
+xgetbv (grub_uint32_t index)
+{
+ grub_uint32_t eax, edx;
+
+ asm volatile ("xgetbv" : "=a" (eax), "=d" (edx) : "c" (index));
+
+ return eax + ((grub_uint64_t)edx << 32);
+}
+
+static void
+xsetbv (grub_uint32_t index, grub_uint64_t value)
+{
+ grub_uint32_t eax = (grub_uint32_t)value;
+ grub_uint32_t edx = (grub_uint32_t)(value >> 32);
+
+ asm volatile ("xsetbv" :: "a" (eax), "d" (edx), "c" (index));
+}
+
+static bool
+enable_avx (void)
+{
+ grub_uint64_t cr4;
+ grub_uint32_t ecx;
+ grub_uint64_t sse_avx_mask = (1 << 2) | (1 << 1);
+ grub_uint64_t xcr0;
+
+ ecx = get_cpuid_ecx ();
+
+ /* Check the following two bits
+ * - CPUID.01H:ECX.XSAVE[bit 26]
+ * If XSAVE is not supported, setting CR4.OSXSAVE will cause
+ * general-protection fault (#GP).
+ * - CPUID.01H:ECX.AVX[bit 28]
+ */
+ grub_dprintf ("hwfeatures", "Check CPUID.01H:ECX 0x%"PRIuGRUB_UINT32_T"\n", ecx);
+ if ((ecx & (5 << 26)) != (5 << 26))
+ return false;
+
+ cr4 = read_cr4 ();
+
+ /* Set CR4.OSXSAVE[bit 18] */
+ if ((cr4 & (1 << 18)) == 0)
+ {
+ grub_dprintf ("hwfeatures", "Set CR4.OSXSAVE\n");
+ cr4 |= 1 << 18;
+ write_cr4 (cr4);
+ }
+
+ ecx = get_cpuid_ecx ();
+
+ /* Check CPUID.01H:ECX.OSXSAVE[bit 27] */
+ if ((ecx & (1 << 27)) == 0)
+ return false;
+
+ xcr0 = old_xcr0 = xgetbv (0);
+
+ /* Set XCR0[bit 1] and XCR0[bit 2] to enable SSE/AVX */
+ if ((xcr0 & sse_avx_mask) != sse_avx_mask)
+ {
+ grub_dprintf ("hwfeatures", "Set XCR0[2:1] to 11b\n");
+ xcr0 |= sse_avx_mask;
+ xsetbv (0, xcr0);
+ }
+
+ return true;
+}
+
+void
+grub_enable_gcry_hwf_x86_64_efi (void)
+{
+ if (enable_sse () == true)
+ hw_features |= HW_FEATURE_X86_64_SSE;
+
+ if (enable_avx () == true)
+ hw_features |= HW_FEATURE_X86_64_AVX;
+}
+
+void
+grub_reset_gcry_hwf_x86_64_efi (void)
+{
+ grub_uint64_t cr0, cr4, xcr0;
+
+ if ((hw_features & HW_FEATURE_X86_64_AVX) != 0)
+ {
+ xcr0 = xgetbv (0);
+ if (xcr0 != old_xcr0)
+ {
+ /*
+ * Reset the AVX state with 'vzeroupper' before clearing XCR0[bit 2].
+ *
+ * Ref: Intel 64 and IA-32 Architectures Software Developer's Manual
+ * - 13.3 ENABLING THE XSAVE FEATURE SET AND XSAVE-ENABLED FEATURES
+ *
+ * "As noted in Section 13.1, the processor will preserve AVX state
+ * unmodified if software clears XCR0[2]. However, clearing XCR0[2]
+ * while AVX state is not in its initial configuration may cause SSE
+ * instructions to incur a power and performance penalty."
+ */
+ asm volatile ("vzeroupper" ::: "memory");
+ xsetbv (0, old_xcr0);
+ }
+ }
+
+ if ((hw_features & HW_FEATURE_X86_64_AVX) != 0 || (hw_features & HW_FEATURE_X86_64_SSE) != 0)
+ {
+ cr4 = read_cr4 ();
+ if (cr4 != old_cr4)
+ write_cr4 (old_cr4);
+ }
+
+ if ((hw_features & HW_FEATURE_X86_64_SSE) != 0)
+ {
+ cr0 = read_cr0 ();
+ if (cr0 != old_cr0)
+ write_cr0 (old_cr0);
+ }
+
+ hw_features = 0;
+}
diff --git a/grub-core/lib/x86_64/setjmp.S b/grub-core/lib/x86_64/setjmp.S
index 6b151bc4d..4c6c81249 100644
--- a/grub-core/lib/x86_64/setjmp.S
+++ b/grub-core/lib/x86_64/setjmp.S
@@ -36,7 +36,7 @@ GRUB_MOD_LICENSE "GPLv3+"
*/
FUNCTION(grub_setjmp)
pop %rsi /* Return address, and adjust the stack */
- xorq %rax, %rax
+ xorl %eax, %eax
movq %rbx, 0(%rdi) /* RBX */
movq %rsp, 8(%rdi) /* RSP */
push %rsi
diff --git a/grub-core/lib/xzembed/xz_dec_stream.c b/grub-core/lib/xzembed/xz_dec_stream.c
index f2dd42dc2..13658d448 100644
--- a/grub-core/lib/xzembed/xz_dec_stream.c
+++ b/grub-core/lib/xzembed/xz_dec_stream.c
@@ -387,8 +387,7 @@ static enum xz_ret hash_validate(struct xz_dec *s, struct xz_buf *b,
&& sizeof (s->hash_value) >= hash->mdlen)
{
hash->final(hash_context);
- grub_memcpy (s->hash_value, hash->read(hash_context),
- hash->mdlen);
+ memcpy (s->hash_value, hash->read (hash_context), hash->mdlen);
s->have_hash_value = 1;
if (s->hash_id == 1 || crc32)
{
diff --git a/grub-core/loader/arm64/xen_boot.c b/grub-core/loader/arm64/xen_boot.c
index 14afec143..ed22b49e0 100644
--- a/grub-core/loader/arm64/xen_boot.c
+++ b/grub-core/loader/arm64/xen_boot.c
@@ -174,7 +174,7 @@ prepare_xen_module_params (struct xen_boot_binary *module, void *xen_boot_fdt)
module->cmdline, module->cmdline, module->cmdline_size);
retval = grub_fdt_set_prop (xen_boot_fdt, module_node, "bootargs",
- module->cmdline, module->cmdline_size + 1);
+ module->cmdline, module->cmdline_size);
if (retval)
return grub_error (GRUB_ERR_IO, "failed to update FDT");
}
diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c
index 2c7c874f2..ded507cd5 100644
--- a/grub-core/loader/efi/linux.c
+++ b/grub-core/loader/efi/linux.c
@@ -55,6 +55,14 @@ static bool initrd_use_loadfile2 = false;
static grub_guid_t load_file2_guid = GRUB_EFI_LOAD_FILE2_PROTOCOL_GUID;
static grub_guid_t device_path_guid = GRUB_EFI_DEVICE_PATH_GUID;
+/*
+ * Clang will produce a warning for missing initializer for the
+ * zero-length array "vendor_defined_data" inside this structure.
+ * Suppress this warning which is treated as an error.
+ */
+#ifdef __clang__
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#endif
static initrd_media_device_path_t initrd_lf2_device_path = {
{
{
@@ -372,6 +380,8 @@ grub_efi_initrd_load_file2 (grub_efi_load_file2_t *this,
if (grub_initrd_load (&initrd_ctx, buffer))
status = GRUB_EFI_DEVICE_ERROR;
+ else
+ *buffer_size = initrd_size;
grub_initrd_close (&initrd_ctx);
return status;
diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c
index 12731feb2..d6e291b2c 100644
--- a/grub-core/loader/i386/linux.c
+++ b/grub-core/loader/i386/linux.c
@@ -234,6 +234,7 @@ grub_e820_add_region (struct grub_boot_e820_entry *e820_entry, int *e820_num,
static grub_err_t
grub_linux_setup_video (struct linux_kernel_params *params)
{
+ struct grub_video_edid_info edid_info;
struct grub_video_mode_info mode_info;
void *framebuffer;
grub_err_t err;
@@ -245,6 +246,19 @@ grub_linux_setup_video (struct linux_kernel_params *params)
if (driver_id == GRUB_VIDEO_DRIVER_NONE)
return 1;
+ err = grub_video_get_edid (&edid_info);
+ if (err != GRUB_ERR_NONE)
+ grub_memset (&edid_info, 0, sizeof (edid_info));
+
+ /*
+ * We cannot transfer any extensions. Therefore clear
+ * the extension flag from the checksum and set the
+ * field to zero. Adding the extension flag to the
+ * checksum does the trick.
+ */
+ edid_info.checksum += edid_info.extension_flag;
+ edid_info.extension_flag = 0;
+
err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
if (err)
@@ -338,6 +352,8 @@ grub_linux_setup_video (struct linux_kernel_params *params)
}
#endif
+ grub_memcpy (params->edid_info, &edid_info, sizeof (params->edid_info));
+
return GRUB_ERR_NONE;
}
diff --git a/grub-core/mmap/mmap.c b/grub-core/mmap/mmap.c
index c8c8312c5..7c7d3911c 100644
--- a/grub-core/mmap/mmap.c
+++ b/grub-core/mmap/mmap.c
@@ -242,6 +242,13 @@ grub_mmap_iterate (grub_memory_hook_t hook, void *hook_data)
else
{
struct mm_list *n = grub_malloc (sizeof (*n));
+ if (n == NULL)
+ {
+ grub_free (ctx.scanline_events);
+ grub_free (present);
+ return grub_errno;
+ }
+
n->val = ctx.scanline_events[i].memtype;
n->present = 1;
n->next = present[ctx.scanline_events[i].priority].next;
diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c
index 2f45a3cc2..fa3834f63 100644
--- a/grub-core/net/bootp.c
+++ b/grub-core/net/bootp.c
@@ -901,14 +901,17 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
err = GRUB_ERR_NONE;
for (j = 0; j < ncards; j++)
{
- grub_free (ifaces[j].name);
if (!ifaces[j].prev)
- continue;
+ {
+ grub_free (ifaces[j].name);
+ continue;
+ }
grub_error_push ();
grub_net_network_level_interface_unregister (&ifaces[j]);
err = grub_error (GRUB_ERR_FILE_NOT_FOUND,
N_("couldn't autoconfigure %s"),
ifaces[j].card->name);
+ grub_free (ifaces[j].name);
}
grub_free (ifaces);
diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c
index f20cd6f83..bef697d98 100644
--- a/grub-core/net/dns.c
+++ b/grub-core/net/dns.c
@@ -424,7 +424,10 @@ recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)),
grub_netbuff_free (nb);
grub_free (redirect_save);
if (!*data->naddresses)
- grub_free (*data->addresses);
+ {
+ grub_free (*data->addresses);
+ *data->addresses = NULL;
+ }
return GRUB_ERR_NONE;
}
diff --git a/grub-core/net/net.c b/grub-core/net/net.c
index 6ea33d1cd..6c94a3b1e 100644
--- a/grub-core/net/net.c
+++ b/grub-core/net/net.c
@@ -2146,6 +2146,7 @@ GRUB_MOD_FINI(net)
grub_unregister_command (cmd_deladdr);
grub_unregister_command (cmd_addroute);
grub_unregister_command (cmd_delroute);
+ grub_unregister_command (cmd_setvlan);
grub_unregister_command (cmd_lsroutes);
grub_unregister_command (cmd_lscards);
grub_unregister_command (cmd_lsaddr);
diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c
index 93dee0caa..064c0ff08 100644
--- a/grub-core/net/tcp.c
+++ b/grub-core/net/tcp.c
@@ -22,6 +22,7 @@
#include
#include
#include
+#include
#define TCP_SYN_RETRANSMISSION_TIMEOUT GRUB_NET_INTERVAL
#define TCP_SYN_RETRANSMISSION_COUNT GRUB_NET_TRIES
@@ -552,6 +553,36 @@ grub_net_tcp_accept (grub_net_tcp_socket_t sock,
return GRUB_ERR_NONE;
}
+/*
+ * Derive a time-based source port to avoid reusing the same port across
+ * reboots. This helps prevent failures caused by server side TCP state
+ * (e.g. TIME_WAIT) from interfering with new connections using the same socket.
+ *
+ * The base port starts at 21550 and increments every second by 8 across
+ * a 5 minute window (300 seconds), giving 2400 possible distinct base ports
+ * per window. In typical GRUB usage, the number of connections per boot is
+ * small, so reuse is effectively avoided.
+ */
+static grub_uint16_t
+get_initial_base_port (void)
+{
+ grub_err_t err;
+ struct grub_datetime date;
+ grub_int64_t t = 0;
+ grub_uint64_t r = 0;
+
+ err = grub_get_datetime (&date);
+ if (err != GRUB_ERR_NONE || !grub_datetime2unixtime (&date, &t))
+ {
+ grub_errno = GRUB_ERR_NONE;
+ return 21550;
+ }
+
+ grub_divmod64 (t, 300, &r);
+
+ return 21550 + (r << 3);
+}
+
grub_net_tcp_socket_t
grub_net_tcp_open (char *server,
grub_uint16_t out_port,
@@ -569,13 +600,19 @@ grub_net_tcp_open (char *server,
struct grub_net_network_level_interface *inf;
grub_net_network_level_address_t gateway;
grub_net_tcp_socket_t socket;
- static grub_uint16_t in_port = 21550;
+ static grub_uint16_t in_port;
struct grub_net_buff *nb;
struct tcphdr *tcph;
int i;
grub_uint8_t *nbd;
grub_net_link_level_address_t ll_target_addr;
+ if (!in_port)
+ {
+ in_port = get_initial_base_port ();
+ grub_dprintf ("net", "base port: %d\n", in_port);
+ }
+
err = grub_net_resolve_address (server, &addr);
if (err)
return NULL;
diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c
index 336b78691..63953bc19 100644
--- a/grub-core/net/tftp.c
+++ b/grub-core/net/tftp.c
@@ -412,7 +412,11 @@ tftp_open (struct grub_file *file, const char *filename)
grub_error_load (&data->save_err);
if (grub_errno)
{
- grub_net_udp_close (data->sock);
+ if (data->sock != NULL)
+ {
+ grub_net_udp_close (data->sock);
+ data->sock = NULL;
+ }
grub_free (data);
file->data = NULL;
return grub_errno;
diff --git a/grub-core/normal/cmdline.c b/grub-core/normal/cmdline.c
index 9c6d9ade9..0875250be 100644
--- a/grub-core/normal/cmdline.c
+++ b/grub-core/normal/cmdline.c
@@ -42,7 +42,14 @@ grub_err_t
grub_set_history (int newsize)
{
grub_uint32_t **old_hist_lines = hist_lines;
+
hist_lines = grub_calloc (newsize, sizeof (grub_uint32_t *));
+ if (hist_lines == NULL)
+ {
+ /* We need to restore hist_lines to avoid memory leak and state loss. */
+ hist_lines = old_hist_lines;
+ return grub_errno;
+ }
/* Copy the old lines into the new buffer. */
if (old_hist_lines)
diff --git a/grub-core/normal/completion.c b/grub-core/normal/completion.c
index 18cadfa85..4058e0a62 100644
--- a/grub-core/normal/completion.c
+++ b/grub-core/normal/completion.c
@@ -490,6 +490,9 @@ grub_normal_do_completion (char *buf, int *restore,
spaces++;
ret = grub_malloc (match_len - current_len + grub_strlen (suffix) + spaces + 1);
+ if (ret == NULL)
+ goto fail;
+
newstr = ret;
for (escstr = match + current_len; *escstr; escstr++)
{
diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
index 96abfda2f..de9a3f961 100644
--- a/grub-core/normal/main.c
+++ b/grub-core/normal/main.c
@@ -21,6 +21,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -67,6 +68,11 @@ grub_normal_free_menu (grub_menu_t menu)
grub_free (entry->args);
}
+ if (entry->blsuki)
+ {
+ entry->blsuki->visible = 0;
+ }
+
grub_free ((void *) entry->id);
grub_free ((void *) entry->users);
grub_free ((void *) entry->title);
@@ -504,7 +510,7 @@ grub_mini_cmd_clear (struct grub_command *cmd __attribute__ ((unused)),
return 0;
}
-static grub_command_t cmd_clear;
+static grub_command_t cmd_clear, cmd_normal, cmd_normal_exit;
static void (*grub_xputs_saved) (const char *str);
static const char *features[] = {
@@ -512,7 +518,8 @@ static const char *features[] = {
"feature_default_font_path", "feature_all_video_module",
"feature_menuentry_id", "feature_menuentry_options", "feature_200_final",
"feature_nativedisk_cmd", "feature_timeout_style",
- "feature_search_cryptodisk_only"
+ "feature_search_cryptodisk_only", "feature_tpm2_cap_pcrs",
+ "feature_gcry_hw_accel"
};
GRUB_MOD_INIT(normal)
@@ -547,10 +554,10 @@ GRUB_MOD_INIT(normal)
grub_env_export ("pager");
/* Register a command "normal" for the rescue mode. */
- grub_register_command ("normal", grub_cmd_normal,
- 0, N_("Enter normal mode."));
- grub_register_command ("normal_exit", grub_cmd_normal_exit,
- 0, N_("Exit from normal mode."));
+ cmd_normal = grub_register_command ("normal", grub_cmd_normal,
+ 0, N_("Enter normal mode."));
+ cmd_normal_exit = grub_register_command ("normal_exit", grub_cmd_normal_exit,
+ 0, N_("Exit from normal mode."));
/* Reload terminal colors when these variables are written to. */
grub_register_variable_hook ("color_normal", NULL, grub_env_write_color_normal);
@@ -592,4 +599,6 @@ GRUB_MOD_FINI(normal)
grub_register_variable_hook ("color_highlight", NULL, NULL);
grub_fs_autoload_hook = 0;
grub_unregister_command (cmd_clear);
+ grub_unregister_command (cmd_normal);
+ grub_unregister_command (cmd_normal_exit);
}
diff --git a/grub-core/osdep/aros/hostdisk.c b/grub-core/osdep/aros/hostdisk.c
index 08723bd45..c75474933 100644
--- a/grub-core/osdep/aros/hostdisk.c
+++ b/grub-core/osdep/aros/hostdisk.c
@@ -207,8 +207,8 @@ grub_util_fd_open (const char *dev, int flg)
sizeof(struct IOExtTD));
if (!ret->ioreq)
{
- free (ret);
DeleteMsgPort (ret->mp);
+ free (ret);
return NULL;
}
@@ -225,9 +225,9 @@ grub_util_fd_open (const char *dev, int flg)
if (OpenDevice ((unsigned char *) tmp, unit,
(struct IORequest *) ret->ioreq, flags))
{
- free (tmp);
- free (ret);
DeleteMsgPort (ret->mp);
+ free (ret);
+ free (tmp);
return NULL;
}
free (tmp);
diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c
index 7872a8d03..d71c373c3 100644
--- a/grub-core/osdep/linux/getroot.c
+++ b/grub-core/osdep/linux/getroot.c
@@ -131,6 +131,7 @@ struct mountinfo_entry
char fstype[ESCAPED_PATH_MAX + 1], device[ESCAPED_PATH_MAX + 1];
};
+#ifdef GRUB_UTIL
static char **
grub_util_raid_getmembers (const char *name, int bootable)
{
@@ -191,6 +192,7 @@ grub_util_raid_getmembers (const char *name, int bootable)
return devicelist;
}
+#endif
/* Statting something on a btrfs filesystem always returns a virtual device
major/minor pair rather than the real underlying device, because btrfs
@@ -579,6 +581,7 @@ out:
return ret;
}
+#ifdef GRUB_UTIL
static char *
get_mdadm_uuid (const char *os_dev)
{
@@ -636,6 +639,7 @@ out:
return name;
}
+#endif
static int
grub_util_is_imsm_or_ddf (const char *os_dev)
@@ -975,6 +979,7 @@ grub_util_part_to_disk (const char *os_dev, struct stat *st,
return path;
}
+#ifdef GRUB_UTIL
static char *
grub_util_get_raid_grub_dev (const char *os_dev)
{
@@ -986,6 +991,8 @@ grub_util_get_raid_grub_dev (const char *os_dev)
char *p, *q;
p = strdup (os_dev + sizeof ("/dev/md_d") - 1);
+ if (p == NULL)
+ return NULL;
q = strchr (p, 'p');
if (q)
@@ -1001,6 +1008,8 @@ grub_util_get_raid_grub_dev (const char *os_dev)
char *p, *q;
p = strdup (os_dev + sizeof ("/dev/md/d") - 1);
+ if (p == NULL)
+ return NULL;
q = strchr (p, 'p');
if (q)
@@ -1014,6 +1023,8 @@ grub_util_get_raid_grub_dev (const char *os_dev)
char *p , *q;
p = strdup (os_dev + sizeof ("/dev/md") - 1);
+ if (p == NULL)
+ return NULL;
q = strchr (p, 'p');
if (q)
@@ -1027,6 +1038,8 @@ grub_util_get_raid_grub_dev (const char *os_dev)
char *p , *q;
p = strdup (os_dev + sizeof ("/dev/md/") - 1);
+ if (p == NULL)
+ return NULL;
q = strchr (p, 'p');
if (q)
@@ -1041,6 +1054,8 @@ grub_util_get_raid_grub_dev (const char *os_dev)
char *p , *q;
p = strdup (os_dev + sizeof ("/dev/md/") - 1);
+ if (p == NULL)
+ return NULL;
q = strchr (p, 'p');
if (q)
@@ -1077,6 +1092,7 @@ grub_util_get_raid_grub_dev (const char *os_dev)
}
return grub_dev;
}
+#endif
enum grub_dev_abstraction_types
grub_util_get_dev_abstraction_os (const char *os_dev)
@@ -1093,6 +1109,7 @@ grub_util_get_dev_abstraction_os (const char *os_dev)
return GRUB_DEV_ABSTRACTION_NONE;
}
+#ifdef GRUB_UTIL
int
grub_util_pull_device_os (const char *os_dev,
enum grub_dev_abstraction_types ab)
@@ -1149,6 +1166,7 @@ grub_util_get_grub_dev_os (const char *os_dev)
return grub_dev;
}
+#endif
char *
grub_make_system_path_relative_to_its_root_os (const char *path)
diff --git a/grub-core/osdep/linux/ofpath.c b/grub-core/osdep/linux/ofpath.c
index a6153d359..24a4d5c8d 100644
--- a/grub-core/osdep/linux/ofpath.c
+++ b/grub-core/osdep/linux/ofpath.c
@@ -695,6 +695,9 @@ strip_trailing_digits (const char *p)
char *new, *end;
new = strdup (p);
+ if (new == NULL)
+ return NULL;
+
end = new + strlen(new) - 1;
while (end >= new)
{
@@ -709,13 +712,18 @@ strip_trailing_digits (const char *p)
char *
grub_util_devname_to_ofpath (const char *sys_devname)
{
- char *name_buf, *device, *devnode, *devicenode, *ofpath;
+ char *name_buf, *device, *devnode, *devicenode, *ofpath = NULL;
name_buf = xrealpath (sys_devname);
device = get_basename (name_buf);
devnode = strip_trailing_digits (name_buf);
+ if (devnode == NULL)
+ goto devnode_fail;
+
devicenode = strip_trailing_digits (device);
+ if (devicenode == NULL)
+ goto devicenode_fail;
if (device[0] == 'h' && device[1] == 'd')
ofpath = of_path_of_ide(name_buf, device, devnode, devicenode);
@@ -741,8 +749,12 @@ grub_util_devname_to_ofpath (const char *sys_devname)
ofpath = NULL;
}
- free (devnode);
free (devicenode);
+
+ devicenode_fail:
+ free (devnode);
+
+ devnode_fail:
free (name_buf);
return ofpath;
diff --git a/grub-core/osdep/unix/getroot.c b/grub-core/osdep/unix/getroot.c
index c7aa202ab..c061735a4 100644
--- a/grub-core/osdep/unix/getroot.c
+++ b/grub-core/osdep/unix/getroot.c
@@ -16,8 +16,8 @@
* along with GRUB. If not, see .
*/
-#include
#include
+#include
#include
#include
@@ -566,6 +566,7 @@ grub_guess_root_devices (const char *dir_in)
#endif
+#ifdef GRUB_UTIL
void
grub_util_pull_lvm_by_command (const char *os_dev)
{
@@ -662,6 +663,7 @@ out:
free (buf);
free (vgid);
}
+#endif
/* ZFS has similar problems to those of btrfs (see above). */
void
diff --git a/grub-core/partmap/msdos.c b/grub-core/partmap/msdos.c
index c85bb74be..bf926175e 100644
--- a/grub-core/partmap/msdos.c
+++ b/grub-core/partmap/msdos.c
@@ -348,6 +348,9 @@ pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors,
* area.
*/
embed_signature_check = grub_malloc (GRUB_DISK_SECTOR_SIZE);
+ if (embed_signature_check == NULL)
+ return grub_errno;
+
for (i = 0; i < *nsectors; i++)
{
if (grub_disk_read (disk, (*sectors)[i], 0, GRUB_DISK_SECTOR_SIZE,
diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c
index da99dfa05..369d985ee 100644
--- a/grub-core/script/execute.c
+++ b/grub-core/script/execute.c
@@ -562,6 +562,8 @@ gettext_append (struct grub_script_argv *result, const char *orig_str)
if (*iptr == '$')
dollar_cnt++;
ctx.allowed_strings = grub_calloc (dollar_cnt, sizeof (ctx.allowed_strings[0]));
+ if (ctx.allowed_strings == NULL)
+ goto fail;
if (parse_string (orig_str, gettext_save_allow, &ctx, 0))
goto fail;
@@ -1015,6 +1017,9 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd)
{
/* As a last resort, try if it is an assignment. */
char *assign = grub_strdup (cmdname);
+ if (assign == NULL)
+ return grub_errno;
+
char *eq = grub_strchr (assign, '=');
if (eq)
diff --git a/grub-core/term/efi/console.c b/grub-core/term/efi/console.c
index 258b52737..37c4e5113 100644
--- a/grub-core/term/efi/console.c
+++ b/grub-core/term/efi/console.c
@@ -219,6 +219,9 @@ grub_efi_translate_key (grub_efi_input_key_t key)
else
return key.unicode_char;
}
+ /* Some devices use power key (key.scan_code 0x0102, suspend) as Enter. */
+ else if (key.scan_code == 0x0102)
+ return '\r';
/* Some devices send enter with scan_code 0x0d (F3) and unicode_char 0x0d. */
else if (key.scan_code == '\r' && key.unicode_char == '\r')
return key.unicode_char;
diff --git a/grub-core/term/efi/serial.c b/grub-core/term/efi/serial.c
index 5dfd2d86c..e409b8d5e 100644
--- a/grub-core/term/efi/serial.c
+++ b/grub-core/term/efi/serial.c
@@ -169,13 +169,13 @@ grub_efiserial_init (void)
port = grub_zalloc (sizeof (*port));
if (!port)
- return;
+ break;
port->name = grub_malloc (sizeof ("efiXXXXXXXXXXXXXXXXXXXX"));
if (!port->name)
{
grub_free (port);
- return;
+ break;
}
grub_snprintf (port->name, sizeof ("efiXXXXXXXXXXXXXXXXXXXX"),
"efi%d", num_serial++);
diff --git a/grub-core/term/ieee1275/serial.c b/grub-core/term/ieee1275/serial.c
index ac2a8f827..b3a8a7c8c 100644
--- a/grub-core/term/ieee1275/serial.c
+++ b/grub-core/term/ieee1275/serial.c
@@ -236,7 +236,11 @@ add_port (struct ofserial_hash_ent *ent)
+ grub_strlen (ent->shortest));
port->elem = ent;
if (!port->name)
- return NULL;
+ {
+ grub_free (port);
+ return NULL;
+ }
+
ptr = grub_stpcpy (port->name, "ieee1275/");
grub_strcpy (ptr, ent->shortest);
diff --git a/grub-core/tests/appended_signature_test.c b/grub-core/tests/appended_signature_test.c
new file mode 100644
index 000000000..2130008bc
--- /dev/null
+++ b/grub-core/tests/appended_signature_test.c
@@ -0,0 +1,348 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2020, 2022 Free Software Foundation, Inc.
+ * Copyright (C) 2020, 2022, 2025 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
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "appended_signatures.h"
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define PROC_FILE(identifier, file_name) \
+ static char *get_##identifier (grub_size_t *sz) \
+ { \
+ char *ret; \
+ \
+ *sz = identifier##_len; \
+ ret = grub_malloc (*sz); \
+ if (ret != NULL) \
+ grub_memcpy (ret, identifier, *sz); \
+ return ret; \
+ } \
+ \
+ static struct grub_procfs_entry identifier##_entry = { .name = file_name, \
+ .get_contents = get_##identifier };
+
+#define DEFINE_TEST_CASE(case_name) PROC_FILE (case_name, #case_name)
+
+#define DO_TEST(case_name, is_valid) \
+ { \
+ grub_procfs_register (#case_name, &case_name##_entry); \
+ do_verify ("(proc)/" #case_name, is_valid); \
+ grub_procfs_unregister (&case_name##_entry); \
+ }
+
+DEFINE_TEST_CASE (hi_signed);
+DEFINE_TEST_CASE (hi_signed_sha256);
+DEFINE_TEST_CASE (hj_signed);
+DEFINE_TEST_CASE (short_msg);
+DEFINE_TEST_CASE (unsigned_msg);
+DEFINE_TEST_CASE (hi_signed_2nd);
+DEFINE_TEST_CASE (hi_double);
+DEFINE_TEST_CASE (hi_double_extended);
+
+PROC_FILE (certificate_der, "certificate.der")
+PROC_FILE (certificate2_der, "certificate2.der")
+PROC_FILE (certificate_printable_der, "certificate_printable.der")
+PROC_FILE (certificate_eku_der, "certificate_eku.der")
+
+static void
+do_verify (const char *f, int is_valid)
+{
+ grub_command_t cmd;
+ char *args[] = { (char *) f, NULL };
+ grub_err_t err;
+
+ cmd = grub_command_find ("append_verify");
+ if (cmd == NULL)
+ {
+ grub_test_assert (0, "can't find command `%s'", "append_verify");
+ return;
+ }
+
+ err = (cmd->func) (cmd, 1, args);
+ if (is_valid)
+ {
+ grub_test_assert (err == GRUB_ERR_NONE, "verification of %s failed: %d: %s",
+ f, grub_errno, grub_errmsg);
+ }
+ else
+ {
+ grub_test_assert (err != GRUB_ERR_NONE,
+ "verification of %s unexpectedly succeeded", f);
+ }
+}
+
+static void
+appended_signature_test (void)
+{
+ grub_command_t cmd_trust, cmd_distrust;
+ char *trust_args[] = { (char *) "(proc)/certificate.der", NULL };
+ char *trust_args2[] = { (char *) "(proc)/certificate2.der", NULL };
+ char *trust_args_printable[] = { (char *) "(proc)/certificate_printable.der", NULL };
+ char *trust_args_eku[] = { (char *) "(proc)/certificate_eku.der", NULL };
+ const char *key_mgmt;
+ grub_err_t err;
+
+ grub_procfs_register ("certificate.der", &certificate_der_entry);
+ grub_procfs_register ("certificate2.der", &certificate2_der_entry);
+ grub_procfs_register ("certificate_printable.der", &certificate_printable_der_entry);
+ grub_procfs_register ("certificate_eku.der", &certificate_eku_der_entry);
+
+ /* Set appended signature key managment to static. */
+ err = grub_env_set ("appendedsig_key_mgmt", "static");
+ grub_test_assert (err == GRUB_ERR_NONE, "set of key management is failed: %d: %s",
+ grub_errno, grub_errmsg);
+
+ /* Get appended signatures key management. */
+ key_mgmt = grub_env_get ("appendedsig_key_mgmt");
+ grub_test_assert (grub_strncmp (key_mgmt, "static", grub_strlen(key_mgmt)) == 0,
+ "getting unexpected key management: %d: %s",
+ grub_errno, grub_errmsg);
+
+ cmd_trust = grub_command_find ("append_add_db_cert");
+ if (cmd_trust == NULL)
+ {
+ grub_test_assert (0, "can't find command `%s'", "append_add_db_cert");
+ return;
+ }
+
+ grub_errno = GRUB_ERR_NONE;
+ err = (cmd_trust->func) (cmd_trust, 1, trust_args);
+ grub_test_assert (err == GRUB_ERR_NONE, "loading certificate failed: %d: %s",
+ grub_errno, grub_errmsg);
+ /* If we have no certificate the remainder of the tests are meaningless. */
+ if (err != GRUB_ERR_NONE)
+ return;
+
+ /*
+ * Reload the command: this works around some 'interesting' behaviour in the
+ * dynamic command dispatcher. The first time you call cmd->func you get a
+ * dispatcher that loads the module, finds the real cmd, calls it, and then
+ * releases some internal storage. This means it's not safe to call a second
+ * time and we need to reload it.
+ */
+ cmd_trust = grub_command_find ("append_add_db_cert");
+
+ /* The hi, signed with key 1, SHA-512. */
+ DO_TEST (hi_signed, 1);
+
+ /* The hi, signed with key 1, SHA-256. */
+ DO_TEST (hi_signed_sha256, 1);
+
+ /* The hi, key 1, SHA-512, second byte corrupted. */
+ DO_TEST (hj_signed, 0);
+
+ /* Message too short for a signature. */
+ DO_TEST (short_msg, 0);
+
+ /* Lorem ipsum. */
+ DO_TEST (unsigned_msg, 0);
+
+ /* The hi, signed with both keys, SHA-512. */
+ DO_TEST (hi_double, 1);
+
+ /*
+ * The hi, signed with both keys and with empty space to test we haven't
+ * broken support for adding more signatures after the fact.
+ */
+ DO_TEST (hi_double_extended, 1);
+
+ /*
+ * In enforcing mode, we shouldn't be able to load a certificate that isn't
+ * signed by an existing trusted key.
+ *
+ * However, procfs files automatically skip the verification test, so we can't
+ * easily test this.
+ */
+
+ /* Verify that testing with 2 trusted certs works. */
+ DO_TEST (hi_signed_2nd, 0);
+
+ err = (cmd_trust->func) (cmd_trust, 1, trust_args);
+ grub_test_assert (err != GRUB_ERR_NONE, "unexpectedly reloaded certificate 1: %d: %s",
+ grub_errno, grub_errmsg);
+
+ err = (cmd_trust->func) (cmd_trust, 1, trust_args2);
+ grub_test_assert (err == GRUB_ERR_NONE, "loading certificate 2 failed: %d: %s",
+ grub_errno, grub_errmsg);
+ if (err != GRUB_ERR_NONE)
+ return;
+
+ DO_TEST (hi_signed_2nd, 1);
+ DO_TEST (hi_signed, 1);
+ DO_TEST (hi_double, 1);
+ DO_TEST (hi_double_extended, 1);
+
+ /*
+ * Check certificate removal. They're added to the _top_ of the db list and
+ * removed by position in the list. Current the list looks like [#2, #1].
+ */
+ cmd_distrust = grub_command_find ("append_add_dbx_cert");
+ if (cmd_distrust == NULL)
+ {
+ grub_test_assert (0, "can't find command `%s'", "append_add_dbx_cert");
+ return;
+ }
+
+ /* Remove the certificate #1. */
+ err = (cmd_distrust->func) (cmd_distrust, 1, trust_args);
+ grub_test_assert (err == GRUB_ERR_NONE, "distrusting certificate 1 failed: %d: %s",
+ grub_errno, grub_errmsg);
+ DO_TEST (hi_signed_2nd, 1);
+ DO_TEST (hi_signed, 0);
+ DO_TEST (hi_double, 1);
+
+ /* Now reload certificate #1. */
+ err = (cmd_trust->func) (cmd_trust, 1, trust_args);
+ grub_test_assert (err == GRUB_ERR_NONE, "reloading certificate 1 failed: %d: %s",
+ grub_errno, grub_errmsg);
+ DO_TEST (hi_signed_2nd, 1);
+ DO_TEST (hi_signed, 1);
+ DO_TEST (hi_double, 1);
+
+ /* Remove the certificate #2. */
+ err = (cmd_distrust->func) (cmd_distrust, 1, trust_args2);
+ grub_test_assert (err == GRUB_ERR_NONE, "distrusting certificate 2 failed: %d: %s",
+ grub_errno, grub_errmsg);
+ DO_TEST (hi_signed_2nd, 0);
+ DO_TEST (hi_signed, 1);
+ DO_TEST (hi_double, 1);
+
+ /* Now reload certificate #2. */
+ err = (cmd_trust->func) (cmd_trust, 1, trust_args2);
+ grub_test_assert (err == GRUB_ERR_NONE, "reloading certificate 2 failed: %d: %s",
+ grub_errno, grub_errmsg);
+ DO_TEST (hi_signed_2nd, 1);
+ DO_TEST (hi_signed, 1);
+ DO_TEST (hi_double, 1);
+
+ /* Remove the certificate #1. */
+ err = (cmd_distrust->func) (cmd_distrust, 1, trust_args);
+ grub_test_assert (err == GRUB_ERR_NONE, "distrusting certificate 1 failed: %d: %s",
+ grub_errno, grub_errmsg);
+
+ /* Remove the certificate #2. */
+ err = (cmd_distrust->func) (cmd_distrust, 1, trust_args2);
+ grub_test_assert (err == GRUB_ERR_NONE, "distrusting certificate 2 failed: %d: %s",
+ grub_errno, grub_errmsg);
+
+ /* Set appended signature key managment to dynamic. */
+ err = grub_env_set ("appendedsig_key_mgmt", "dynamic");
+ grub_test_assert (err == GRUB_ERR_NONE, "set of key management is failed: %d: %s",
+ grub_errno, grub_errmsg);
+
+ /* Get appended signatures key management. */
+ key_mgmt = grub_env_get ("appendedsig_key_mgmt");
+ grub_test_assert (grub_strncmp (key_mgmt, "dynamic", grub_strlen(key_mgmt)) == 0,
+ "getting unexpected key management: %d: %s",
+ grub_errno, grub_errmsg);
+
+ cmd_trust = grub_command_find ("append_add_db_cert");
+ err = (cmd_trust->func) (cmd_trust, 1, trust_args);
+ grub_test_assert ((err == GRUB_ERR_NONE || err == GRUB_ERR_EXISTS || GRUB_ERR_ACCESS_DENIED),
+ "loading certificate 1 failed: %d: %s",
+ grub_errno, grub_errmsg);
+ if (err != GRUB_ERR_NONE)
+ return;
+
+ DO_TEST (hi_signed, 1);
+ DO_TEST (hi_double, 1);
+ DO_TEST (hi_double_extended, 1);
+
+ err = (cmd_trust->func) (cmd_trust, 1, trust_args2);
+ grub_test_assert ((err == GRUB_ERR_NONE || err == GRUB_ERR_EXISTS || GRUB_ERR_ACCESS_DENIED),
+ "loading certificate 2 failed: %d: %s",
+ grub_errno, grub_errmsg);
+ if (err != GRUB_ERR_NONE)
+ return;
+
+ DO_TEST (hi_signed_2nd, 1);
+ DO_TEST (hi_signed, 1);
+ DO_TEST (hi_double, 1);
+ DO_TEST (hi_double_extended, 1);
+
+ cmd_distrust = grub_command_find ("append_add_dbx_cert");
+ if (cmd_distrust == NULL)
+ {
+ grub_test_assert (0, "can't find command `%s'", "append_add_dbx_cert");
+ return;
+ }
+
+ /* Now remove certificate #1. */
+ err = (cmd_distrust->func) (cmd_distrust, 1, trust_args);
+ grub_test_assert ((err == GRUB_ERR_NONE || err == GRUB_ERR_EXISTS),
+ "distrusting certificate 1 failed: %d: %s",
+ grub_errno, grub_errmsg);
+ DO_TEST (hi_signed_2nd, 1);
+ DO_TEST (hi_signed, 0);
+ DO_TEST (hi_double, 1);
+
+ /* Now reload certificate #1. */
+ err = (cmd_trust->func) (cmd_trust, 1, trust_args);
+ grub_test_assert (err != GRUB_ERR_NONE, "unexpectedly reloaded certificate 1: %d: %s",
+ grub_errno, grub_errmsg);
+ DO_TEST (hi_signed_2nd, 1);
+ DO_TEST (hi_signed, 0);
+ DO_TEST (hi_double, 1);
+
+ /* Remove the certificate #2. */
+ err = (cmd_distrust->func) (cmd_distrust, 1, trust_args2);
+ grub_test_assert ((err == GRUB_ERR_NONE || err == GRUB_ERR_EXISTS),
+ "distrusting certificate 2 failed: %d: %s",
+ grub_errno, grub_errmsg);
+ DO_TEST (hi_signed_2nd, 0);
+ DO_TEST (hi_signed, 0);
+ DO_TEST (hi_double, 0);
+
+ /* Now reload certificate #2. */
+ err = (cmd_trust->func) (cmd_trust, 1, trust_args2);
+ grub_test_assert (err != GRUB_ERR_NONE, "unexpectedly reloaded certificate 2: %d: %s",
+ grub_errno, grub_errmsg);
+ DO_TEST (hi_signed_2nd, 0);
+ DO_TEST (hi_signed, 0);
+ DO_TEST (hi_double, 0);
+
+ /*
+ * Lastly, check a certificate that uses printableString rather than utf8String
+ * loads properly, and that a certificate with an appropriate extended key usage
+ * loads.
+ */
+ err = (cmd_trust->func) (cmd_trust, 1, trust_args_printable);
+ grub_test_assert (err == GRUB_ERR_NONE, "trusting printable certificate failed: %d: %s",
+ grub_errno, grub_errmsg);
+
+ err = (cmd_trust->func) (cmd_trust, 1, trust_args_eku);
+ grub_test_assert (err == GRUB_ERR_NONE, "trusting certificate with extended key usage failed: %d: %s",
+ grub_errno, grub_errmsg);
+
+ grub_procfs_unregister (&certificate_der_entry);
+ grub_procfs_unregister (&certificate2_der_entry);
+ grub_procfs_unregister (&certificate_printable_der_entry);
+ grub_procfs_unregister (&certificate_eku_der_entry);
+}
+
+GRUB_FUNCTIONAL_TEST (appended_signature_test, appended_signature_test);
diff --git a/grub-core/tests/appended_signatures.h b/grub-core/tests/appended_signatures.h
new file mode 100644
index 000000000..c6aa12d86
--- /dev/null
+++ b/grub-core/tests/appended_signatures.h
@@ -0,0 +1,975 @@
+unsigned char certificate_der[] = {
+ 0x30, 0x82, 0x05, 0x5d, 0x30, 0x82, 0x03, 0x45, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5,
+ 0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+ 0x05, 0x00, 0x30, 0x3d, 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x0c, 0x32, 0x47, 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65,
+ 0x6e, 0x64, 0x65, 0x64, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75,
+ 0x72, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x31, 0x30,
+ 0x36, 0x32, 0x39, 0x30, 0x38, 0x33, 0x36, 0x31, 0x33, 0x5a, 0x18, 0x0f,
+ 0x32, 0x31, 0x32, 0x31, 0x30, 0x36, 0x30, 0x35, 0x30, 0x38, 0x33, 0x36,
+ 0x31, 0x33, 0x5a, 0x30, 0x33, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x0c, 0x28, 0x47, 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70,
+ 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74,
+ 0x75, 0x72, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x69, 0x67,
+ 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x65, 0x79, 0x30, 0x82, 0x02, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a,
+ 0x02, 0x82, 0x02, 0x01, 0x00, 0xb9, 0x09, 0xb2, 0xf6, 0x24, 0x34, 0xdc,
+ 0x62, 0xe6, 0x4e, 0xee, 0x04, 0xdb, 0x29, 0xdc, 0x94, 0xcc, 0xee, 0x8a,
+ 0x5b, 0xc3, 0x9e, 0x06, 0xba, 0xa7, 0x9b, 0xa4, 0x5f, 0x15, 0x59, 0x8e,
+ 0xb8, 0x6e, 0x3c, 0xeb, 0x2e, 0xf2, 0xac, 0x21, 0x42, 0xbd, 0x30, 0xa1,
+ 0x39, 0xe5, 0xb9, 0x4f, 0xa0, 0x53, 0xd5, 0x42, 0xdc, 0x8a, 0x87, 0x30,
+ 0x38, 0x93, 0x44, 0x80, 0x3b, 0x1a, 0x7e, 0x9e, 0x8e, 0x3e, 0xea, 0x45,
+ 0xa0, 0x11, 0x8b, 0xfb, 0x78, 0xe4, 0xbc, 0x65, 0x6b, 0x73, 0xea, 0x6e,
+ 0xdf, 0x7c, 0x5b, 0x63, 0x7e, 0x5b, 0x0a, 0x1c, 0xe6, 0x76, 0x19, 0xb5,
+ 0x01, 0xde, 0xf6, 0x65, 0x51, 0x30, 0x0a, 0x56, 0x69, 0x69, 0xe8, 0x20,
+ 0xf9, 0x13, 0xf1, 0xbf, 0x6f, 0xdd, 0xce, 0x94, 0x96, 0x6e, 0x63, 0xd6,
+ 0xfa, 0xa4, 0x91, 0x5f, 0xb3, 0x9c, 0xc7, 0xfa, 0xa9, 0xff, 0x66, 0x5f,
+ 0xf3, 0xab, 0x5e, 0xdf, 0x4e, 0xca, 0x11, 0xcf, 0xbf, 0xf8, 0xad, 0x65,
+ 0xb1, 0x49, 0x8b, 0xe9, 0x2a, 0xad, 0x7d, 0xf3, 0x0b, 0xfa, 0x5b, 0x6a,
+ 0x6a, 0x20, 0x12, 0x77, 0xef, 0x4b, 0xb6, 0xbe, 0x92, 0xba, 0x14, 0x9c,
+ 0x5e, 0xea, 0xdc, 0x56, 0x6d, 0x92, 0xd3, 0x64, 0x22, 0xf6, 0x12, 0xe8,
+ 0x7d, 0x5e, 0x9c, 0xd6, 0xf9, 0x75, 0x68, 0x7f, 0x8f, 0xd3, 0x6e, 0x05,
+ 0x94, 0x91, 0x4f, 0xa1, 0xd6, 0x50, 0x72, 0x3b, 0x11, 0x1f, 0x28, 0x13,
+ 0xe8, 0x25, 0x6b, 0xdf, 0xff, 0x72, 0x46, 0x25, 0xe9, 0x05, 0x6f, 0x02,
+ 0xc7, 0x1e, 0xc9, 0xcf, 0x99, 0xe9, 0xa7, 0xe2, 0xae, 0xbc, 0xc1, 0x22,
+ 0x32, 0x73, 0x2d, 0xa3, 0x70, 0x8f, 0xa7, 0x8d, 0xbf, 0x5f, 0x74, 0x05,
+ 0x1b, 0x5e, 0xfe, 0x97, 0x3c, 0xe7, 0x3b, 0x86, 0x0d, 0xf6, 0x38, 0xdb,
+ 0xd2, 0x39, 0x47, 0x82, 0x00, 0x44, 0x6c, 0x7b, 0x40, 0x24, 0x0b, 0x3a,
+ 0xd4, 0x19, 0x31, 0xba, 0x4e, 0x8e, 0xa3, 0x33, 0xa6, 0x78, 0xef, 0x72,
+ 0x9f, 0x06, 0x37, 0x01, 0x9b, 0x79, 0x0d, 0x04, 0xbf, 0xba, 0xd5, 0x1f,
+ 0x27, 0xdc, 0x85, 0xbb, 0xef, 0xd2, 0x60, 0xda, 0xa0, 0x3f, 0x66, 0xce,
+ 0x9f, 0xa2, 0x7e, 0xa8, 0x8d, 0xee, 0x14, 0x4b, 0xcb, 0x93, 0xf1, 0x38,
+ 0xac, 0x4f, 0xd8, 0x29, 0xf3, 0x6f, 0xd4, 0xfd, 0x4d, 0x34, 0x77, 0x58,
+ 0x99, 0xdb, 0x16, 0xc1, 0xd0, 0xc7, 0x43, 0x41, 0x70, 0xc4, 0xad, 0x01,
+ 0x29, 0x65, 0x22, 0x43, 0x00, 0x6f, 0xb3, 0x00, 0x27, 0x38, 0xc1, 0x4f,
+ 0xda, 0x28, 0x96, 0x42, 0xdc, 0xbc, 0x3e, 0x34, 0x8e, 0x14, 0xb8, 0xf3,
+ 0x86, 0x4a, 0xea, 0x16, 0x90, 0xf9, 0x0e, 0x9e, 0x8f, 0x66, 0x0c, 0xbf,
+ 0x29, 0xd3, 0x8f, 0xfc, 0x4d, 0x38, 0x68, 0xe2, 0xe7, 0x64, 0x32, 0x47,
+ 0xdd, 0x56, 0xc9, 0xe4, 0x47, 0x9f, 0x18, 0x89, 0xfc, 0x30, 0x7a, 0xae,
+ 0x63, 0xe4, 0xec, 0x93, 0x04, 0xd4, 0x61, 0xe7, 0xbf, 0x0a, 0x06, 0x29,
+ 0xc2, 0xa6, 0xd5, 0x53, 0x5d, 0x65, 0x6d, 0x4a, 0xd0, 0xb7, 0x68, 0x4d,
+ 0x46, 0x0a, 0xb5, 0xff, 0x52, 0x5e, 0x92, 0x7e, 0x75, 0x08, 0xa4, 0x63,
+ 0x0a, 0x6c, 0x31, 0x7a, 0xaa, 0x0c, 0x52, 0xf4, 0x2e, 0xcd, 0x08, 0xeb,
+ 0xb3, 0xbd, 0xad, 0x8b, 0x8b, 0x9b, 0x8d, 0x71, 0x42, 0x30, 0x8e, 0xc7,
+ 0xfd, 0xec, 0xb7, 0xe6, 0x26, 0x96, 0xf2, 0x74, 0x1b, 0x78, 0x95, 0x22,
+ 0x14, 0xf3, 0xc9, 0xd3, 0x79, 0x11, 0xd9, 0xb7, 0x4d, 0x0d, 0x61, 0x60,
+ 0x5c, 0x47, 0x50, 0xf3, 0xca, 0x84, 0x4c, 0x5c, 0x30, 0x2c, 0x6a, 0x18,
+ 0x26, 0xb0, 0xf3, 0xd1, 0x15, 0x19, 0x39, 0xc3, 0x23, 0x13, 0x0f, 0x9c,
+ 0x97, 0x2b, 0x97, 0x93, 0xf9, 0xf8, 0x18, 0x9b, 0x4a, 0x4d, 0xd6, 0xd3,
+ 0xf5, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30, 0x0c,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00,
+ 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x07,
+ 0x80, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x8f, 0xba, 0x8b, 0xf5, 0xf4, 0x77, 0xb2, 0xa4, 0x19, 0xef, 0x43, 0xb1,
+ 0x8b, 0x03, 0x4b, 0x45, 0x47, 0xb5, 0x2a, 0x48, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x59, 0x1c, 0xb5,
+ 0x52, 0x62, 0x83, 0x05, 0x3b, 0x41, 0x4c, 0x63, 0x4d, 0x5b, 0xf4, 0x8c,
+ 0xe6, 0xd7, 0xda, 0x87, 0x54, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01,
+ 0x00, 0x36, 0x2d, 0x0a, 0xcb, 0x49, 0x54, 0x75, 0xd7, 0xca, 0x21, 0x86,
+ 0xae, 0x40, 0x0f, 0x63, 0x10, 0x35, 0xfd, 0xbc, 0xba, 0x28, 0x31, 0x33,
+ 0x07, 0x08, 0x64, 0x03, 0x6c, 0xd3, 0xd5, 0xf7, 0xb7, 0x79, 0x11, 0x0c,
+ 0xa8, 0x9e, 0xfd, 0x34, 0xa2, 0xba, 0x77, 0x15, 0x15, 0x2d, 0x2c, 0x96,
+ 0xae, 0x47, 0xbb, 0x82, 0x89, 0x09, 0x7f, 0xd1, 0x95, 0x69, 0x9b, 0xfe,
+ 0xd7, 0x6f, 0x4e, 0x68, 0xf6, 0xe7, 0x5f, 0x54, 0xa1, 0x3a, 0xeb, 0xa4,
+ 0xbf, 0x7a, 0xb6, 0x7f, 0xaa, 0xd8, 0xd7, 0x99, 0xcb, 0xae, 0x88, 0x6d,
+ 0x7a, 0xf3, 0xfa, 0x9e, 0x44, 0x2f, 0x30, 0xa8, 0xe6, 0xb9, 0x75, 0xa0,
+ 0x82, 0xd6, 0xb0, 0xe3, 0x03, 0xb3, 0x12, 0xa3, 0xdc, 0xb9, 0x4d, 0x93,
+ 0xd4, 0x30, 0xea, 0xce, 0x96, 0x92, 0x07, 0xf8, 0xba, 0xe4, 0x0f, 0x41,
+ 0xe3, 0x04, 0xaa, 0x8c, 0x07, 0x1a, 0x34, 0x60, 0xfc, 0xc0, 0x05, 0xd2,
+ 0x5a, 0xa8, 0x66, 0xef, 0xe0, 0x94, 0xc5, 0x2f, 0x0f, 0xff, 0xdc, 0x70,
+ 0xfb, 0xe2, 0x9d, 0x61, 0x51, 0x25, 0x02, 0xff, 0x4b, 0x69, 0xfd, 0x66,
+ 0xb9, 0xeb, 0x0c, 0xc8, 0x50, 0xd3, 0xb1, 0x08, 0x1e, 0x09, 0x54, 0x87,
+ 0xe8, 0xa3, 0x4b, 0xef, 0x0c, 0x32, 0x0a, 0x6c, 0xec, 0x27, 0x22, 0xba,
+ 0x7f, 0xdc, 0x52, 0x27, 0x31, 0x14, 0x9a, 0xa8, 0xf7, 0xf9, 0xeb, 0xc8,
+ 0xb5, 0x8d, 0x12, 0xed, 0x94, 0xab, 0x3d, 0x9a, 0xfb, 0x4e, 0x04, 0x05,
+ 0xd2, 0x3c, 0x7c, 0x8a, 0xed, 0x46, 0x1b, 0x7c, 0xb5, 0x6c, 0x40, 0xb8,
+ 0xc1, 0xbf, 0xb0, 0xd2, 0x93, 0x8e, 0xa8, 0x0f, 0xde, 0x78, 0xf3, 0x8c,
+ 0xd8, 0x9f, 0xf8, 0xdc, 0xa1, 0x23, 0x20, 0x40, 0x17, 0xb4, 0xdb, 0xb7,
+ 0x09, 0x74, 0xa7, 0x80, 0xc2, 0x12, 0xd9, 0x76, 0x79, 0x5b, 0x71, 0xa9,
+ 0x6c, 0xd4, 0x57, 0x48, 0xe8, 0xfe, 0xc5, 0xc2, 0x6e, 0xe7, 0x83, 0x5a,
+ 0x07, 0xf0, 0x33, 0xc1, 0xc1, 0x1d, 0x34, 0xd4, 0xc8, 0xb0, 0xb7, 0xdb,
+ 0xeb, 0xe9, 0xe3, 0x59, 0xdc, 0x7f, 0x36, 0x58, 0xa9, 0xb8, 0x52, 0xdd,
+ 0xf9, 0xfd, 0x1c, 0x22, 0x2f, 0x93, 0x3d, 0x53, 0x89, 0x80, 0xde, 0xa2,
+ 0xb5, 0xa5, 0x36, 0xbd, 0xc3, 0x92, 0x03, 0xf3, 0x93, 0xc8, 0xc7, 0x4a,
+ 0x0b, 0x8b, 0x62, 0xfe, 0xd0, 0xf8, 0x0d, 0x7a, 0x32, 0xb4, 0x39, 0x1a,
+ 0xb7, 0x4e, 0xaa, 0xc4, 0x33, 0x32, 0x90, 0x8c, 0xab, 0xd4, 0xae, 0xa5,
+ 0xa4, 0x85, 0xcf, 0xba, 0xe1, 0x1b, 0x26, 0x7f, 0x74, 0x02, 0x12, 0x09,
+ 0x89, 0x56, 0xe4, 0xe7, 0x9d, 0x91, 0xde, 0x88, 0xe7, 0x1c, 0xed, 0x80,
+ 0x05, 0xa8, 0x58, 0x9a, 0x3e, 0x16, 0x97, 0xd5, 0xbc, 0x54, 0xcc, 0xf0,
+ 0x32, 0xf2, 0x93, 0x09, 0x94, 0x9f, 0x3c, 0xd9, 0x58, 0xca, 0x68, 0x0b,
+ 0xde, 0x3f, 0x73, 0x64, 0xb7, 0xf4, 0xd7, 0x5f, 0x2b, 0xe7, 0x7b, 0x06,
+ 0xca, 0xb1, 0x3e, 0xed, 0xd2, 0xb9, 0x29, 0xc1, 0x95, 0x87, 0xad, 0xd6,
+ 0x63, 0x69, 0xb8, 0x1f, 0x70, 0xdb, 0xeb, 0xc7, 0x11, 0x7d, 0xe2, 0x99,
+ 0x64, 0x6a, 0xf5, 0x3f, 0x30, 0x74, 0x5f, 0x2a, 0x21, 0xda, 0xef, 0x44,
+ 0x1d, 0xad, 0x97, 0xa1, 0xfe, 0x14, 0xa7, 0x88, 0x99, 0xd0, 0x1e, 0xb0,
+ 0x61, 0x88, 0x09, 0xc9, 0xfa, 0xd1, 0xb3, 0xcb, 0x1d, 0x76, 0x04, 0xbe,
+ 0x06, 0x44, 0xd2, 0x30, 0x5e, 0x95, 0x4b, 0x96, 0xc0, 0xd6, 0xbe, 0xd0,
+ 0x4d, 0xf2, 0xf4, 0x71, 0x72, 0xa9, 0xbd, 0x07, 0x4f, 0xbc, 0xb3, 0x78,
+ 0xb4, 0x8a, 0x44, 0xbd, 0x58, 0xd5, 0x21, 0xb6, 0x47, 0x9c, 0x88, 0x1f,
+ 0xbc, 0xbd, 0x54, 0xfa, 0x1d, 0x49, 0xec, 0x51, 0xd9, 0x43, 0x49, 0x9c,
+ 0x0c, 0xfa, 0x18, 0xdb, 0xeb, 0x05, 0x77, 0xa2, 0x9a
+};
+unsigned int certificate_der_len = 1377;
+
+unsigned char hi_signed[] = {
+ 0x68, 0x69, 0x0a, 0x30, 0x82, 0x02, 0xb4, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa5, 0x30, 0x82,
+ 0x02, 0xa1, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
+ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02,
+ 0x7e, 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d,
+ 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47,
+ 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64,
+ 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54,
+ 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+ 0x79, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5,
+ 0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30,
+ 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77,
+ 0x97, 0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03,
+ 0xdf, 0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e,
+ 0x77, 0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52,
+ 0x55, 0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01,
+ 0xc2, 0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75,
+ 0x88, 0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1,
+ 0xd1, 0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d,
+ 0xe3, 0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e,
+ 0x45, 0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f,
+ 0x86, 0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae,
+ 0xcd, 0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5,
+ 0x27, 0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a,
+ 0xf1, 0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33,
+ 0x1a, 0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2,
+ 0x8d, 0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9,
+ 0xc5, 0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70,
+ 0xa1, 0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67,
+ 0x04, 0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a,
+ 0x9a, 0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3,
+ 0x1c, 0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33,
+ 0xd6, 0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4,
+ 0xe9, 0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1,
+ 0x60, 0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26,
+ 0x83, 0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41,
+ 0xd7, 0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda,
+ 0x35, 0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3,
+ 0x0f, 0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98,
+ 0xa0, 0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5,
+ 0xaa, 0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e,
+ 0xe2, 0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe,
+ 0xc3, 0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c,
+ 0x24, 0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6,
+ 0xe6, 0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5,
+ 0x5e, 0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f,
+ 0x74, 0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb,
+ 0x3b, 0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0,
+ 0x8a, 0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4,
+ 0xdc, 0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b,
+ 0x83, 0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52,
+ 0x21, 0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f,
+ 0x51, 0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c,
+ 0x19, 0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17,
+ 0x35, 0x23, 0x2d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0xb8, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73,
+ 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70,
+ 0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a
+};
+unsigned int hi_signed_len = 739;
+
+unsigned char hj_signed[] = {
+ 0x68, 0x6a, 0x0a, 0x30, 0x82, 0x02, 0xb4, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa5, 0x30, 0x82,
+ 0x02, 0xa1, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
+ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02,
+ 0x7e, 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d,
+ 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47,
+ 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64,
+ 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54,
+ 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+ 0x79, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5,
+ 0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30,
+ 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77,
+ 0x97, 0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03,
+ 0xdf, 0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e,
+ 0x77, 0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52,
+ 0x55, 0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01,
+ 0xc2, 0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75,
+ 0x88, 0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1,
+ 0xd1, 0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d,
+ 0xe3, 0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e,
+ 0x45, 0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f,
+ 0x86, 0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae,
+ 0xcd, 0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5,
+ 0x27, 0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a,
+ 0xf1, 0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33,
+ 0x1a, 0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2,
+ 0x8d, 0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9,
+ 0xc5, 0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70,
+ 0xa1, 0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67,
+ 0x04, 0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a,
+ 0x9a, 0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3,
+ 0x1c, 0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33,
+ 0xd6, 0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4,
+ 0xe9, 0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1,
+ 0x60, 0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26,
+ 0x83, 0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41,
+ 0xd7, 0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda,
+ 0x35, 0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3,
+ 0x0f, 0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98,
+ 0xa0, 0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5,
+ 0xaa, 0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e,
+ 0xe2, 0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe,
+ 0xc3, 0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c,
+ 0x24, 0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6,
+ 0xe6, 0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5,
+ 0x5e, 0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f,
+ 0x74, 0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb,
+ 0x3b, 0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0,
+ 0x8a, 0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4,
+ 0xdc, 0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b,
+ 0x83, 0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52,
+ 0x21, 0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f,
+ 0x51, 0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c,
+ 0x19, 0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17,
+ 0x35, 0x23, 0x2d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0xb8, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73,
+ 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70,
+ 0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a
+};
+unsigned int hj_signed_len = 739;
+
+unsigned char hi_signed_sha256[] = {
+ 0x68, 0x69, 0x0a, 0x30, 0x82, 0x02, 0xb4, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa5, 0x30, 0x82,
+ 0x02, 0xa1, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
+ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x30, 0x0b, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02,
+ 0x7e, 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d,
+ 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47,
+ 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64,
+ 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54,
+ 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+ 0x79, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5,
+ 0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30,
+ 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0x96, 0x02, 0x7b, 0xa4, 0x07,
+ 0xa7, 0x39, 0x8d, 0xa6, 0x0b, 0xde, 0x33, 0xdd, 0xf8, 0xec, 0x24, 0x5d,
+ 0x06, 0x81, 0xe7, 0x3c, 0x2d, 0x0c, 0x53, 0xfb, 0x7e, 0x5a, 0xf3, 0xee,
+ 0xe5, 0x4c, 0x7c, 0xf7, 0xe7, 0x8f, 0x36, 0x62, 0x35, 0xb8, 0x99, 0xc3,
+ 0xeb, 0x85, 0x1d, 0x2e, 0x40, 0x6e, 0x2a, 0xb4, 0x3a, 0x76, 0x48, 0x4f,
+ 0x8b, 0x29, 0xd4, 0x9e, 0x5c, 0xd2, 0x41, 0x4d, 0xc1, 0x72, 0x0f, 0x97,
+ 0xe0, 0x7d, 0x88, 0xed, 0x1a, 0xb0, 0xde, 0x1b, 0x21, 0xa6, 0x0c, 0x19,
+ 0xd8, 0xb0, 0x12, 0x54, 0x7b, 0xd8, 0x19, 0x03, 0xbd, 0x77, 0x83, 0x23,
+ 0xeb, 0xeb, 0x68, 0x0a, 0x7b, 0x3a, 0x4d, 0x25, 0x44, 0xe1, 0x64, 0x8d,
+ 0x43, 0x5a, 0x1c, 0x9f, 0x74, 0x79, 0x31, 0x3f, 0xc7, 0x8e, 0xae, 0xe1,
+ 0xf9, 0x1e, 0x54, 0x12, 0x36, 0x85, 0xf2, 0x55, 0xba, 0x42, 0x60, 0x64,
+ 0x25, 0x9f, 0x73, 0x62, 0x42, 0xd2, 0x1c, 0x5e, 0x39, 0x4f, 0x7d, 0x91,
+ 0xb8, 0xf9, 0x59, 0x3c, 0x13, 0x6b, 0x84, 0x76, 0x6d, 0x8a, 0xc3, 0xcb,
+ 0x2d, 0x14, 0x27, 0x16, 0xdc, 0x20, 0x2c, 0xbc, 0x6b, 0xc9, 0xda, 0x9f,
+ 0xef, 0xe2, 0x2d, 0xc3, 0x83, 0xd8, 0xf9, 0x94, 0x18, 0xbc, 0xfe, 0x8f,
+ 0xa9, 0x44, 0xad, 0xff, 0x1b, 0xcb, 0x86, 0x30, 0x96, 0xa8, 0x3c, 0x7a,
+ 0x4b, 0x73, 0x1b, 0xa9, 0xc3, 0x3b, 0xaa, 0xd7, 0x44, 0xa8, 0x4d, 0xd6,
+ 0x92, 0xb6, 0x00, 0x04, 0x09, 0x05, 0x4a, 0x95, 0x02, 0x90, 0x19, 0x8c,
+ 0x9a, 0xa5, 0xee, 0x58, 0x24, 0xb0, 0xca, 0x5e, 0x6f, 0x73, 0xdb, 0xf5,
+ 0xa1, 0xf4, 0xf0, 0xa9, 0xeb, 0xe4, 0xdc, 0x55, 0x9f, 0x8f, 0x7a, 0xd0,
+ 0xf7, 0xb6, 0xaa, 0xa6, 0xb5, 0xb4, 0xab, 0xb8, 0x65, 0xad, 0x12, 0x32,
+ 0x1c, 0xe6, 0x99, 0x71, 0x93, 0xe8, 0xb4, 0x1e, 0x21, 0x27, 0x52, 0xea,
+ 0x8c, 0xc8, 0x79, 0x96, 0x2e, 0x48, 0x60, 0x57, 0x1c, 0x7d, 0x8c, 0x0d,
+ 0x07, 0xa7, 0x12, 0x83, 0x0a, 0x76, 0x6a, 0x64, 0xed, 0xbe, 0x8d, 0xaf,
+ 0xdf, 0x51, 0x05, 0xdd, 0xf2, 0xd3, 0xb8, 0x93, 0xa9, 0x13, 0xa5, 0x96,
+ 0xe8, 0xfa, 0x82, 0x02, 0x18, 0x71, 0x7a, 0x71, 0xbb, 0x39, 0x6f, 0x85,
+ 0xee, 0x16, 0x82, 0x27, 0x42, 0x9f, 0x83, 0xc8, 0xab, 0x6a, 0x3b, 0x99,
+ 0xba, 0x38, 0x92, 0x38, 0xae, 0x59, 0xfa, 0xaa, 0x40, 0x2b, 0x52, 0x95,
+ 0xca, 0x5e, 0xe1, 0x9b, 0x00, 0xbd, 0xb9, 0x63, 0x25, 0x8d, 0xc7, 0x22,
+ 0xaf, 0xe5, 0x67, 0x76, 0x91, 0xf4, 0xda, 0xc9, 0x7e, 0x9e, 0xec, 0x9b,
+ 0x1f, 0x7d, 0x3b, 0xfe, 0xa1, 0x20, 0x52, 0xac, 0xd0, 0xe5, 0xa6, 0xf1,
+ 0xfd, 0x4c, 0x08, 0x59, 0x7d, 0x50, 0xbb, 0x0c, 0xcf, 0xd8, 0xb6, 0x0f,
+ 0xc7, 0x19, 0xcb, 0x7a, 0x96, 0x6f, 0x0f, 0x6c, 0x71, 0x56, 0x72, 0xd1,
+ 0x06, 0x29, 0x0f, 0x08, 0xa2, 0x46, 0x3e, 0x58, 0x42, 0xc4, 0x8c, 0xe0,
+ 0x6e, 0xe9, 0x37, 0xd5, 0x2f, 0x74, 0x36, 0x1d, 0x14, 0xcb, 0x10, 0x0e,
+ 0x7d, 0x67, 0xbd, 0x38, 0x0e, 0xa4, 0x27, 0x1d, 0x3c, 0x78, 0x4d, 0x0d,
+ 0x15, 0x42, 0x70, 0x20, 0xe0, 0x1d, 0x83, 0x6c, 0x4d, 0xf1, 0x02, 0xa1,
+ 0x51, 0xc4, 0xc5, 0x5d, 0x69, 0x90, 0x58, 0x82, 0x94, 0x50, 0x36, 0x22,
+ 0xb3, 0xa4, 0x15, 0x77, 0xdc, 0x44, 0xb0, 0x50, 0xa2, 0x3f, 0xd0, 0x0e,
+ 0x1b, 0xfc, 0xf4, 0x5b, 0x3b, 0x7d, 0x63, 0x94, 0x22, 0xf3, 0x87, 0x0a,
+ 0x41, 0x8a, 0x27, 0x48, 0xcb, 0x6c, 0xfd, 0x70, 0x66, 0x5f, 0x11, 0x6f,
+ 0x74, 0x2c, 0x42, 0xaf, 0x74, 0x45, 0x3f, 0x0c, 0x03, 0xc8, 0x80, 0xe2,
+ 0x71, 0x08, 0x93, 0xbd, 0x4d, 0x18, 0x78, 0x1e, 0x8e, 0xb9, 0x3a, 0xd6,
+ 0x1a, 0xde, 0xf9, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0xb8, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73,
+ 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70,
+ 0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a
+};
+unsigned int hi_signed_sha256_len = 739;
+
+unsigned char short_msg[] = {
+ 0x68, 0x69, 0x0a
+};
+unsigned int short_msg_len = 3;
+
+unsigned char unsigned_msg[] = {
+ 0x53, 0x65, 0x64, 0x20, 0x75, 0x74, 0x20, 0x70, 0x65, 0x72, 0x73, 0x70,
+ 0x69, 0x63, 0x69, 0x61, 0x74, 0x69, 0x73, 0x20, 0x75, 0x6e, 0x64, 0x65,
+ 0x20, 0x6f, 0x6d, 0x6e, 0x69, 0x73, 0x20, 0x69, 0x73, 0x74, 0x65, 0x20,
+ 0x6e, 0x61, 0x74, 0x75, 0x73, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20,
+ 0x73, 0x69, 0x74, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74,
+ 0x65, 0x6d, 0x20, 0x61, 0x63, 0x63, 0x75, 0x73, 0x61, 0x6e, 0x74, 0x69,
+ 0x75, 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, 0x71, 0x75,
+ 0x65, 0x20, 0x6c, 0x61, 0x75, 0x64, 0x61, 0x6e, 0x74, 0x69, 0x75, 0x6d,
+ 0x2c, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6d, 0x20, 0x72, 0x65, 0x6d, 0x20,
+ 0x61, 0x70, 0x65, 0x72, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x65, 0x61, 0x71,
+ 0x75, 0x65, 0x20, 0x69, 0x70, 0x73, 0x61, 0x20, 0x71, 0x75, 0x61, 0x65,
+ 0x20, 0x61, 0x62, 0x20, 0x69, 0x6c, 0x6c, 0x6f, 0x20, 0x69, 0x6e, 0x76,
+ 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x76, 0x65, 0x72, 0x69, 0x74,
+ 0x61, 0x74, 0x69, 0x73, 0x20, 0x65, 0x74, 0x20, 0x71, 0x75, 0x61, 0x73,
+ 0x69, 0x20, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x6f,
+ 0x20, 0x62, 0x65, 0x61, 0x74, 0x61, 0x65, 0x20, 0x76, 0x69, 0x74, 0x61,
+ 0x65, 0x20, 0x64, 0x69, 0x63, 0x74, 0x61, 0x20, 0x73, 0x75, 0x6e, 0x74,
+ 0x20, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x62, 0x6f, 0x2e, 0x20,
+ 0x4e, 0x65, 0x6d, 0x6f, 0x20, 0x65, 0x6e, 0x69, 0x6d, 0x20, 0x69, 0x70,
+ 0x73, 0x61, 0x6d, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74,
+ 0x65, 0x6d, 0x20, 0x71, 0x75, 0x69, 0x61, 0x20, 0x76, 0x6f, 0x6c, 0x75,
+ 0x70, 0x74, 0x61, 0x73, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x73, 0x70,
+ 0x65, 0x72, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x20, 0x61, 0x75, 0x74, 0x20,
+ 0x6f, 0x64, 0x69, 0x74, 0x20, 0x61, 0x75, 0x74, 0x20, 0x66, 0x75, 0x67,
+ 0x69, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x64, 0x20, 0x71, 0x75, 0x69, 0x61,
+ 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x75, 0x6e, 0x74, 0x75,
+ 0x72, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f,
+ 0x72, 0x65, 0x73, 0x20, 0x65, 0x6f, 0x73, 0x20, 0x71, 0x75, 0x69, 0x20,
+ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x20, 0x76, 0x6f, 0x6c, 0x75,
+ 0x70, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x20, 0x73, 0x65, 0x71, 0x75, 0x69,
+ 0x20, 0x6e, 0x65, 0x73, 0x63, 0x69, 0x75, 0x6e, 0x74, 0x2e, 0x20, 0x4e,
+ 0x65, 0x71, 0x75, 0x65, 0x20, 0x70, 0x6f, 0x72, 0x72, 0x6f, 0x20, 0x71,
+ 0x75, 0x69, 0x73, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x65, 0x73, 0x74, 0x2c,
+ 0x20, 0x71, 0x75, 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d,
+ 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x71, 0x75, 0x69, 0x61, 0x20,
+ 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d,
+ 0x65, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x74, 0x65,
+ 0x74, 0x75, 0x72, 0x2c, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69, 0x73, 0x63,
+ 0x69, 0x20, 0x76, 0x65, 0x6c, 0x69, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x64,
+ 0x20, 0x71, 0x75, 0x69, 0x61, 0x20, 0x6e, 0x6f, 0x6e, 0x20, 0x6e, 0x75,
+ 0x6d, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x65, 0x69, 0x75, 0x73, 0x20, 0x6d,
+ 0x6f, 0x64, 0x69, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x20,
+ 0x69, 0x6e, 0x63, 0x69, 0x64, 0x75, 0x6e, 0x74, 0x20, 0x75, 0x74, 0x20,
+ 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x65, 0x20, 0x65, 0x74, 0x20, 0x64, 0x6f,
+ 0x6c, 0x6f, 0x72, 0x65, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x61, 0x6d, 0x20,
+ 0x61, 0x6c, 0x69, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x71, 0x75, 0x61, 0x65,
+ 0x72, 0x61, 0x74, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74,
+ 0x65, 0x6d, 0x2e, 0x20, 0x55, 0x74, 0x20, 0x65, 0x6e, 0x69, 0x6d, 0x20,
+ 0x61, 0x64, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x61, 0x20, 0x76, 0x65,
+ 0x6e, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x71, 0x75, 0x69, 0x73, 0x20, 0x6e,
+ 0x6f, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x20, 0x65, 0x78, 0x65, 0x72, 0x63,
+ 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x6d, 0x20, 0x75, 0x6c,
+ 0x6c, 0x61, 0x6d, 0x20, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x69, 0x73,
+ 0x20, 0x73, 0x75, 0x73, 0x63, 0x69, 0x70, 0x69, 0x74, 0x20, 0x6c, 0x61,
+ 0x62, 0x6f, 0x72, 0x69, 0x6f, 0x73, 0x61, 0x6d, 0x2c, 0x20, 0x6e, 0x69,
+ 0x73, 0x69, 0x20, 0x75, 0x74, 0x20, 0x61, 0x6c, 0x69, 0x71, 0x75, 0x69,
+ 0x64, 0x20, 0x65, 0x78, 0x20, 0x65, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d,
+ 0x6f, 0x64, 0x69, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x61,
+ 0x74, 0x75, 0x72, 0x3f, 0x20, 0x51, 0x75, 0x69, 0x73, 0x20, 0x61, 0x75,
+ 0x74, 0x65, 0x6d, 0x20, 0x76, 0x65, 0x6c, 0x20, 0x65, 0x75, 0x6d, 0x20,
+ 0x69, 0x75, 0x72, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x68, 0x65,
+ 0x6e, 0x64, 0x65, 0x72, 0x69, 0x74, 0x20, 0x71, 0x75, 0x69, 0x20, 0x69,
+ 0x6e, 0x20, 0x65, 0x61, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61,
+ 0x74, 0x65, 0x20, 0x76, 0x65, 0x6c, 0x69, 0x74, 0x20, 0x65, 0x73, 0x73,
+ 0x65, 0x20, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x6e, 0x69, 0x68, 0x69, 0x6c,
+ 0x20, 0x6d, 0x6f, 0x6c, 0x65, 0x73, 0x74, 0x69, 0x61, 0x65, 0x20, 0x63,
+ 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x61, 0x74, 0x75, 0x72, 0x2c, 0x20,
+ 0x76, 0x65, 0x6c, 0x20, 0x69, 0x6c, 0x6c, 0x75, 0x6d, 0x20, 0x71, 0x75,
+ 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x65, 0x75,
+ 0x6d, 0x20, 0x66, 0x75, 0x67, 0x69, 0x61, 0x74, 0x20, 0x71, 0x75, 0x6f,
+ 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x73, 0x20, 0x6e, 0x75,
+ 0x6c, 0x6c, 0x61, 0x20, 0x70, 0x61, 0x72, 0x69, 0x61, 0x74, 0x75, 0x72,
+ 0x3f, 0x0a
+};
+unsigned int unsigned_msg_len = 866;
+
+unsigned char certificate2_der[] = {
+ 0x30, 0x82, 0x05, 0x52, 0x30, 0x82, 0x03, 0x3a, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5,
+ 0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+ 0x05, 0x00, 0x30, 0x3a, 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x0c, 0x2f, 0x47, 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20,
+ 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x74, 0x79, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x37, 0x32, 0x38,
+ 0x31, 0x33, 0x32, 0x34, 0x32, 0x39, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x32,
+ 0x30, 0x30, 0x37, 0x30, 0x34, 0x31, 0x33, 0x32, 0x34, 0x32, 0x39, 0x5a,
+ 0x30, 0x2b, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
+ 0x20, 0x47, 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65,
+ 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69,
+ 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x65, 0x79, 0x30, 0x82, 0x02,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02,
+ 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xb0, 0x2f, 0x50, 0x01, 0x9c, 0x0e,
+ 0xd6, 0x8c, 0x07, 0xca, 0xc1, 0xcf, 0xbc, 0x03, 0xdd, 0xd3, 0xfa, 0xe3,
+ 0x4f, 0x71, 0xc1, 0x30, 0xaa, 0x09, 0x96, 0xe4, 0xd0, 0x6c, 0x42, 0x93,
+ 0xdb, 0x35, 0xf6, 0x7e, 0x1b, 0x67, 0xc0, 0xc2, 0x2d, 0x5b, 0xec, 0xca,
+ 0x35, 0x06, 0x32, 0x6c, 0x7b, 0x2c, 0xd3, 0x71, 0x2b, 0xe9, 0x7a, 0x19,
+ 0xd1, 0xf2, 0xa0, 0x7f, 0xd7, 0x4d, 0x6e, 0x28, 0xbb, 0xae, 0x49, 0x4a,
+ 0xbc, 0xea, 0x47, 0x67, 0xb8, 0x36, 0xa6, 0xf5, 0x0d, 0x0e, 0x20, 0x14,
+ 0x0c, 0x66, 0x67, 0x28, 0xb5, 0x97, 0x8b, 0x1f, 0x5e, 0x32, 0x06, 0x29,
+ 0x9c, 0x99, 0x92, 0x0f, 0x73, 0xac, 0xfd, 0xd2, 0x1d, 0xf2, 0xa8, 0x55,
+ 0x9d, 0x1b, 0xd8, 0x3d, 0xb0, 0x76, 0x9a, 0xb6, 0x6c, 0x9f, 0x62, 0x37,
+ 0x2f, 0xc0, 0xef, 0x44, 0xb3, 0x0d, 0x4a, 0x3e, 0x4f, 0x7d, 0xbd, 0xdb,
+ 0xd8, 0x75, 0x5f, 0x68, 0xe3, 0xf0, 0xec, 0x82, 0x66, 0x7c, 0x31, 0x70,
+ 0xa9, 0xa1, 0x6f, 0x38, 0x9f, 0xdf, 0xf5, 0xf0, 0x7d, 0x23, 0x9d, 0x34,
+ 0xa5, 0x85, 0xd3, 0xdf, 0x68, 0x41, 0xfc, 0x4f, 0x89, 0x45, 0x3c, 0x24,
+ 0x81, 0xa6, 0xf2, 0x3c, 0x02, 0x26, 0x09, 0x48, 0xdd, 0xfe, 0x4b, 0xb6,
+ 0x66, 0xbf, 0x8f, 0xe5, 0x5f, 0xf0, 0x5d, 0x8a, 0x61, 0x2e, 0x5f, 0x9f,
+ 0x80, 0xd9, 0xd5, 0xe6, 0x41, 0xd8, 0x10, 0x5e, 0x7a, 0xc6, 0xdb, 0x89,
+ 0xc7, 0xca, 0x6c, 0x5b, 0xb1, 0x4e, 0x7d, 0x0c, 0x03, 0xfd, 0x50, 0xca,
+ 0xbf, 0xbb, 0xe2, 0x69, 0x4b, 0x4e, 0xc2, 0x3d, 0x75, 0xfa, 0xd1, 0xcc,
+ 0xd6, 0xf9, 0x39, 0xb9, 0xdc, 0x53, 0xad, 0x62, 0xfb, 0x1b, 0x94, 0x26,
+ 0x7f, 0x21, 0x54, 0x5c, 0xb7, 0xdc, 0xe7, 0x96, 0x8c, 0xce, 0x75, 0xe0,
+ 0x17, 0x01, 0x3a, 0x3c, 0x77, 0x6e, 0xa4, 0x8b, 0x7a, 0x83, 0x28, 0x7a,
+ 0xf7, 0xb0, 0x5f, 0xfc, 0x7f, 0x2d, 0x2e, 0xec, 0xf5, 0xeb, 0x9c, 0x63,
+ 0x74, 0xd0, 0xe5, 0xdc, 0x19, 0xe4, 0x71, 0xc5, 0x4a, 0x8a, 0x54, 0xa4,
+ 0xe0, 0x7d, 0x4e, 0xbf, 0x53, 0x30, 0xaf, 0xd0, 0xeb, 0x96, 0xc3, 0xbb,
+ 0x65, 0xf7, 0x67, 0xf5, 0xae, 0xd3, 0x96, 0xf2, 0x63, 0xc8, 0x69, 0xf7,
+ 0x47, 0xcb, 0x27, 0x79, 0xe1, 0xff, 0x2f, 0x68, 0xdf, 0x1e, 0xb3, 0xb8,
+ 0x0c, 0xc5, 0x58, 0x73, 0xcc, 0xfe, 0x8c, 0xda, 0x4e, 0x3b, 0x01, 0x04,
+ 0xcd, 0xcb, 0xb8, 0x3e, 0x06, 0xfd, 0x4c, 0x0a, 0x9f, 0x5e, 0x76, 0x8c,
+ 0x0c, 0x83, 0x75, 0x09, 0x08, 0xb2, 0xdb, 0xf4, 0x49, 0x4e, 0xa0, 0xf2,
+ 0x0c, 0x7b, 0x87, 0x38, 0x9e, 0x22, 0x67, 0xbd, 0xd1, 0x97, 0x57, 0x24,
+ 0xf1, 0x46, 0x07, 0xf9, 0xd2, 0x1b, 0xec, 0x25, 0x5e, 0x67, 0xd9, 0x66,
+ 0x23, 0x1b, 0xd3, 0xe4, 0xaa, 0xec, 0x88, 0xf0, 0x7e, 0x15, 0x83, 0x51,
+ 0x31, 0x67, 0x51, 0x76, 0x5f, 0x55, 0xd7, 0x36, 0xdf, 0x4a, 0x84, 0x0b,
+ 0x6f, 0x5c, 0xbb, 0x5b, 0x8f, 0x37, 0x23, 0x7f, 0xf8, 0x17, 0x84, 0xa2,
+ 0x70, 0x20, 0x07, 0x0c, 0x90, 0x3a, 0x04, 0xfd, 0xf0, 0x08, 0x4a, 0xb1,
+ 0x16, 0x0f, 0xe6, 0xf6, 0x40, 0x51, 0x83, 0xd2, 0x87, 0x40, 0x9c, 0x1c,
+ 0x9f, 0x13, 0x38, 0x17, 0xd3, 0x34, 0x58, 0xad, 0x05, 0x71, 0xa0, 0x73,
+ 0xca, 0x40, 0xa6, 0xa4, 0x81, 0x02, 0xee, 0xa8, 0x72, 0x41, 0xa1, 0x41,
+ 0x18, 0x64, 0x8a, 0x86, 0x8a, 0x5d, 0xe6, 0x4f, 0x0a, 0xc5, 0x95, 0x98,
+ 0xf9, 0x78, 0xfe, 0x19, 0x0d, 0xc9, 0xb3, 0x89, 0xc1, 0x2b, 0x09, 0xbe,
+ 0xf1, 0xd2, 0x04, 0x5d, 0xcc, 0x28, 0xf5, 0x4b, 0xd2, 0x20, 0x4f, 0xc5,
+ 0x41, 0x9d, 0x8c, 0x85, 0xd8, 0xb0, 0x68, 0x5e, 0xc1, 0x0c, 0xb7, 0x24,
+ 0x4d, 0x67, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30,
+ 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30,
+ 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02,
+ 0x07, 0x80, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0xac, 0xf5, 0x47, 0x17, 0xd9, 0x7d, 0xc1, 0xb1, 0xc4, 0x41, 0xe1,
+ 0x41, 0x60, 0xcb, 0x37, 0x11, 0x60, 0x28, 0x78, 0x5f, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x21, 0x94,
+ 0xfb, 0xf9, 0xb2, 0x43, 0xe9, 0x33, 0xd7, 0x50, 0x7d, 0xc7, 0x37, 0xdb,
+ 0xd5, 0x82, 0x5a, 0x4e, 0xbe, 0x1b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02,
+ 0x01, 0x00, 0x96, 0x70, 0x65, 0x26, 0x42, 0xf8, 0xdc, 0x69, 0xde, 0xcf,
+ 0x41, 0x3a, 0x2e, 0x7f, 0x5b, 0xf1, 0xf9, 0x3b, 0x9b, 0xd2, 0x4e, 0x64,
+ 0x48, 0x81, 0xe4, 0x5d, 0x1e, 0x22, 0xce, 0x68, 0x63, 0x62, 0xe5, 0x1b,
+ 0x9b, 0xf2, 0xc7, 0x12, 0xda, 0x1e, 0x9b, 0x90, 0x84, 0x79, 0x48, 0x12,
+ 0xe6, 0x21, 0x6f, 0x2f, 0x7e, 0x18, 0x77, 0xdb, 0x8c, 0xc4, 0xd1, 0x0d,
+ 0x91, 0xbf, 0x39, 0x22, 0x0f, 0x64, 0xcf, 0x25, 0x2e, 0x8c, 0x1f, 0x91,
+ 0x81, 0xb5, 0xe9, 0x6c, 0x02, 0x3a, 0xf8, 0x07, 0xa2, 0x6f, 0x46, 0x5d,
+ 0x7b, 0xfd, 0x43, 0xff, 0x41, 0x0f, 0xe2, 0x57, 0x1c, 0xbd, 0x48, 0x60,
+ 0x53, 0x11, 0x48, 0x87, 0x88, 0x9d, 0x13, 0x82, 0x40, 0x68, 0x44, 0x2c,
+ 0xc6, 0xc8, 0x95, 0x27, 0x4f, 0xb6, 0xb9, 0x4a, 0x22, 0x0a, 0xfd, 0xe4,
+ 0x46, 0x8f, 0x35, 0x12, 0x98, 0x5a, 0x34, 0x6f, 0x2b, 0x57, 0x62, 0xa1,
+ 0x4d, 0x8d, 0x79, 0x37, 0xe4, 0x6b, 0x8a, 0x32, 0x5b, 0xcb, 0xef, 0x79,
+ 0x11, 0xed, 0xa7, 0xf8, 0x7a, 0x1c, 0xbd, 0x86, 0xdc, 0x0e, 0x2e, 0xfd,
+ 0xd3, 0x51, 0xbb, 0x73, 0xad, 0x00, 0xa0, 0x1b, 0xf9, 0x1d, 0xd1, 0x4a,
+ 0xe4, 0xd4, 0x02, 0x63, 0x2b, 0x39, 0x5f, 0x18, 0x08, 0x2f, 0x42, 0xb7,
+ 0x23, 0x4b, 0x48, 0x46, 0x1f, 0x63, 0x87, 0xae, 0x6d, 0xd5, 0xdb, 0x60,
+ 0xf8, 0x5f, 0xd3, 0x13, 0xec, 0xca, 0xdd, 0x60, 0x60, 0x79, 0x52, 0x70,
+ 0x47, 0xae, 0x1d, 0x38, 0x78, 0x71, 0xcf, 0xb3, 0x04, 0x03, 0xbe, 0xba,
+ 0x81, 0xba, 0x74, 0xb1, 0x30, 0x35, 0xdc, 0xea, 0x21, 0x4a, 0x9b, 0x70,
+ 0xfb, 0xd6, 0x60, 0x59, 0x78, 0x0c, 0x4d, 0x39, 0x19, 0x1d, 0xe5, 0x75,
+ 0xba, 0x07, 0xf4, 0x22, 0x37, 0x64, 0xb7, 0xf2, 0x9a, 0xc9, 0x11, 0x2d,
+ 0x8e, 0x58, 0xa6, 0xcf, 0x83, 0xf1, 0xcb, 0x6c, 0x7f, 0x02, 0xbd, 0xda,
+ 0x03, 0x92, 0xa9, 0x45, 0x24, 0x56, 0xc5, 0xbd, 0x41, 0xd1, 0x20, 0x86,
+ 0xc0, 0xb6, 0xb7, 0xe8, 0xa7, 0xb2, 0x46, 0xf7, 0x8e, 0xa9, 0x38, 0x0e,
+ 0x23, 0x77, 0x3c, 0x0d, 0x66, 0x83, 0x6a, 0x1a, 0x6b, 0x7f, 0x54, 0x11,
+ 0x58, 0x0d, 0x4a, 0xb5, 0x74, 0x60, 0xca, 0xed, 0xff, 0x91, 0x47, 0xd9,
+ 0x29, 0xe0, 0xaa, 0x8c, 0xa8, 0x8f, 0x10, 0x4c, 0x15, 0x7d, 0xce, 0x95,
+ 0xf9, 0x87, 0x1e, 0x18, 0x38, 0x18, 0xfc, 0xcc, 0xaf, 0x91, 0x17, 0x3f,
+ 0xfa, 0xf0, 0x8a, 0x09, 0x6f, 0xba, 0x4e, 0x53, 0xf7, 0xfa, 0x4f, 0x20,
+ 0xa3, 0xf4, 0x4a, 0x5a, 0xde, 0x17, 0x1c, 0x29, 0x6a, 0x6f, 0x03, 0x48,
+ 0xdf, 0xad, 0x4f, 0xe4, 0xbc, 0x71, 0xc4, 0x72, 0x32, 0x11, 0x84, 0xac,
+ 0x09, 0xd2, 0x18, 0x44, 0x35, 0xf1, 0xcd, 0xaf, 0xa8, 0x98, 0xe0, 0x8b,
+ 0xec, 0xa0, 0x83, 0x37, 0xc3, 0x35, 0x85, 0xd6, 0xd8, 0x1b, 0xe0, 0x75,
+ 0xdc, 0xfd, 0xde, 0xc9, 0xeb, 0xd5, 0x18, 0x0f, 0xd3, 0x4c, 0x2f, 0x71,
+ 0xdc, 0x48, 0xe3, 0x14, 0xeb, 0xda, 0x00, 0x24, 0x24, 0x9e, 0xa3, 0x8e,
+ 0x3e, 0x08, 0x6f, 0x22, 0x24, 0xd6, 0xc4, 0x85, 0x8f, 0x68, 0x00, 0x4a,
+ 0x82, 0x4c, 0x33, 0x6e, 0xa5, 0x35, 0x7b, 0xeb, 0x4b, 0xdc, 0xa0, 0xa6,
+ 0x65, 0x6f, 0x5a, 0x7a, 0xdf, 0x8a, 0x01, 0x52, 0xa1, 0x6c, 0xff, 0x59,
+ 0x22, 0x7f, 0xe1, 0x96, 0x1b, 0x19, 0xb8, 0xf9, 0x5d, 0x44, 0x9f, 0x91,
+ 0x03, 0x3c, 0x3d, 0xa1, 0x2a, 0xb6, 0x5a, 0x51, 0xa0, 0xce, 0x4a, 0x88,
+ 0x22, 0x72, 0x9c, 0xdc, 0xc0, 0x47, 0x76, 0x35, 0x84, 0x75, 0x9b, 0x87,
+ 0x5c, 0xd3, 0xcf, 0xe7, 0xdd, 0xa3, 0x57, 0x14, 0xdf, 0x00, 0xfd, 0x19,
+ 0x2a, 0x7d, 0x89, 0x27, 0x1c, 0x78, 0x97, 0x04, 0x58, 0x48
+};
+unsigned int certificate2_der_len = 1366;
+
+unsigned char hi_signed_2nd[] = {
+ 0x68, 0x69, 0x0a, 0x30, 0x82, 0x02, 0xb1, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa2, 0x30, 0x82,
+ 0x02, 0x9e, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
+ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02,
+ 0x7b, 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x01, 0x30, 0x52, 0x30, 0x3a,
+ 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2f, 0x47,
+ 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74,
+ 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
+ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x02, 0x14,
+ 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, 0x91, 0x07,
+ 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, 0x0b, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x04, 0x82, 0x02, 0x00, 0x0e, 0xc2, 0x30, 0x38, 0x81, 0x23, 0x68, 0x90,
+ 0xae, 0x5f, 0xce, 0xf7, 0x27, 0xb1, 0x8c, 0x2e, 0x12, 0x10, 0xc6, 0x99,
+ 0xdc, 0x4d, 0x4b, 0x79, 0xda, 0xe4, 0x32, 0x10, 0x46, 0x1c, 0x16, 0x07,
+ 0x87, 0x66, 0x55, 0xff, 0x64, 0x1c, 0x61, 0x25, 0xd5, 0xb9, 0xe1, 0xfe,
+ 0xea, 0x5a, 0xcd, 0x56, 0xa5, 0xc3, 0xbe, 0xb1, 0x61, 0xc7, 0x6f, 0x5f,
+ 0x69, 0x20, 0x64, 0x50, 0x6f, 0x12, 0x78, 0xb6, 0x0c, 0x72, 0x44, 0x4f,
+ 0x60, 0x0f, 0x9f, 0xa2, 0x83, 0x3b, 0xc2, 0x83, 0xd5, 0x14, 0x1f, 0x6f,
+ 0x3e, 0xb2, 0x47, 0xb5, 0x58, 0xc5, 0xa7, 0xb4, 0x82, 0x53, 0x2e, 0x53,
+ 0x95, 0x4e, 0x3d, 0xe4, 0x62, 0xe8, 0xa1, 0xaf, 0xae, 0xbf, 0xa9, 0xd2,
+ 0x22, 0x07, 0xbe, 0x71, 0x37, 0x2c, 0x5a, 0xa7, 0x6c, 0xaf, 0x14, 0xc0,
+ 0x6c, 0x2f, 0xbf, 0x4f, 0x15, 0xc2, 0x0f, 0x8b, 0xdc, 0x68, 0x45, 0xdf,
+ 0xf3, 0xa5, 0x7f, 0x11, 0x6a, 0x54, 0xcd, 0x67, 0xb9, 0x2e, 0x7d, 0x05,
+ 0xe3, 0x1c, 0x1d, 0xcc, 0x77, 0x8e, 0x97, 0xb1, 0xa0, 0x11, 0x09, 0x3d,
+ 0x90, 0x54, 0xfc, 0x7e, 0xbb, 0xbb, 0x21, 0x23, 0x03, 0x44, 0xbf, 0x7d,
+ 0x2c, 0xc9, 0x15, 0x42, 0xe5, 0xa0, 0x3b, 0xa2, 0xd1, 0x5b, 0x73, 0x81,
+ 0xff, 0xfa, 0x90, 0xfc, 0x27, 0x7b, 0x2f, 0x86, 0x9c, 0x1d, 0x14, 0x36,
+ 0x94, 0xa2, 0x6e, 0xe8, 0x9d, 0xa0, 0x5f, 0xfc, 0x5a, 0x0d, 0xa4, 0xd5,
+ 0x2f, 0x8d, 0xd6, 0x00, 0xfa, 0x93, 0x5b, 0x09, 0x7f, 0x42, 0x78, 0xcc,
+ 0x8c, 0x49, 0xda, 0xd9, 0xf6, 0x43, 0xe7, 0xe1, 0x3c, 0xa2, 0xe2, 0x70,
+ 0xe2, 0x6a, 0x99, 0xc5, 0xd6, 0xa2, 0xe3, 0x0b, 0xd4, 0x09, 0xac, 0x94,
+ 0xaf, 0xb7, 0xf0, 0xb3, 0x0c, 0x1e, 0xf5, 0x16, 0x4f, 0x53, 0x9a, 0xe3,
+ 0xcc, 0xe2, 0x0c, 0x4a, 0xb9, 0xe6, 0x06, 0xbb, 0xf7, 0x41, 0x43, 0x20,
+ 0x04, 0xee, 0x99, 0x2f, 0xd8, 0x9f, 0xda, 0x3f, 0xfd, 0x49, 0xb8, 0xc2,
+ 0xbd, 0xd9, 0xc5, 0x72, 0xfd, 0xe3, 0xce, 0x1c, 0xbc, 0xe4, 0x39, 0xac,
+ 0x2a, 0x99, 0xe9, 0xb4, 0x3e, 0x74, 0x10, 0xeb, 0xd5, 0x14, 0xcc, 0xdb,
+ 0xf1, 0x04, 0x63, 0x36, 0xfb, 0x1f, 0x2b, 0xe2, 0x73, 0xd4, 0xd8, 0x49,
+ 0x31, 0xa8, 0x55, 0xcc, 0xa7, 0x76, 0x36, 0x6e, 0x18, 0xdc, 0xb9, 0xb0,
+ 0x29, 0x99, 0xcf, 0x49, 0xbf, 0xf9, 0xdb, 0x7f, 0x24, 0x42, 0x02, 0xcb,
+ 0xc1, 0xaa, 0xcb, 0xba, 0x18, 0x85, 0x86, 0xc7, 0xf4, 0x1c, 0x62, 0x76,
+ 0xbc, 0x73, 0xfb, 0xe4, 0x15, 0xb8, 0xdd, 0x5d, 0xa6, 0x68, 0x39, 0xa5,
+ 0x3d, 0x33, 0xaf, 0xd5, 0x92, 0x4d, 0x48, 0xdb, 0x22, 0xc0, 0xdc, 0x49,
+ 0x5f, 0x7b, 0xa8, 0xd2, 0x62, 0x2d, 0xa7, 0x39, 0x93, 0x48, 0xe7, 0x6b,
+ 0x23, 0xba, 0xd4, 0xe0, 0xc1, 0x29, 0x55, 0xc4, 0x34, 0xe3, 0xac, 0x25,
+ 0xa7, 0x15, 0xad, 0xab, 0xb3, 0xb7, 0x25, 0xca, 0x37, 0x88, 0x40, 0x2e,
+ 0x47, 0x6e, 0x92, 0x20, 0x09, 0x2e, 0x5a, 0xec, 0xf2, 0xfb, 0xb3, 0xa0,
+ 0x16, 0xb6, 0x93, 0xf2, 0xf5, 0x8b, 0xfe, 0xaf, 0x25, 0xee, 0x2e, 0x98,
+ 0x6c, 0x0a, 0xfe, 0xae, 0x0b, 0x57, 0xf5, 0x9f, 0x3c, 0x80, 0xe9, 0x8b,
+ 0xaf, 0x92, 0x8a, 0xad, 0xe7, 0xa0, 0xe4, 0xe6, 0x0a, 0xa0, 0xc7, 0x83,
+ 0xb5, 0x48, 0x58, 0x5f, 0x55, 0x9e, 0x9b, 0x27, 0xcd, 0x31, 0x1f, 0x3e,
+ 0x50, 0x5a, 0x91, 0xad, 0x21, 0x1b, 0x97, 0x5b, 0xe8, 0xfa, 0x29, 0x8a,
+ 0xa4, 0x17, 0xe8, 0xab, 0x87, 0x02, 0xd6, 0x18, 0x8c, 0x9f, 0x65, 0xb7,
+ 0x2a, 0xfa, 0xde, 0x5f, 0x77, 0x30, 0x6c, 0x04, 0x22, 0xe6, 0x58, 0x26,
+ 0x14, 0x0d, 0x9c, 0x41, 0x0a, 0x82, 0x77, 0xdb, 0x40, 0xa1, 0x58, 0xac,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xb5,
+ 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e,
+ 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64,
+ 0x65, 0x64, 0x7e, 0x0a
+};
+unsigned int hi_signed_2nd_len = 736;
+
+unsigned char hi_double[] = {
+ 0x68, 0x69, 0x0a, 0x30, 0x82, 0x05, 0x2f, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x05, 0x20, 0x30, 0x82,
+ 0x05, 0x1c, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
+ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x04,
+ 0xf9, 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x01, 0x30, 0x52, 0x30, 0x3a,
+ 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2f, 0x47,
+ 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74,
+ 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
+ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x02, 0x14,
+ 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, 0x91, 0x07,
+ 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, 0x0b, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x04, 0x82, 0x02, 0x00, 0x0e, 0xc2, 0x30, 0x38, 0x81, 0x23, 0x68, 0x90,
+ 0xae, 0x5f, 0xce, 0xf7, 0x27, 0xb1, 0x8c, 0x2e, 0x12, 0x10, 0xc6, 0x99,
+ 0xdc, 0x4d, 0x4b, 0x79, 0xda, 0xe4, 0x32, 0x10, 0x46, 0x1c, 0x16, 0x07,
+ 0x87, 0x66, 0x55, 0xff, 0x64, 0x1c, 0x61, 0x25, 0xd5, 0xb9, 0xe1, 0xfe,
+ 0xea, 0x5a, 0xcd, 0x56, 0xa5, 0xc3, 0xbe, 0xb1, 0x61, 0xc7, 0x6f, 0x5f,
+ 0x69, 0x20, 0x64, 0x50, 0x6f, 0x12, 0x78, 0xb6, 0x0c, 0x72, 0x44, 0x4f,
+ 0x60, 0x0f, 0x9f, 0xa2, 0x83, 0x3b, 0xc2, 0x83, 0xd5, 0x14, 0x1f, 0x6f,
+ 0x3e, 0xb2, 0x47, 0xb5, 0x58, 0xc5, 0xa7, 0xb4, 0x82, 0x53, 0x2e, 0x53,
+ 0x95, 0x4e, 0x3d, 0xe4, 0x62, 0xe8, 0xa1, 0xaf, 0xae, 0xbf, 0xa9, 0xd2,
+ 0x22, 0x07, 0xbe, 0x71, 0x37, 0x2c, 0x5a, 0xa7, 0x6c, 0xaf, 0x14, 0xc0,
+ 0x6c, 0x2f, 0xbf, 0x4f, 0x15, 0xc2, 0x0f, 0x8b, 0xdc, 0x68, 0x45, 0xdf,
+ 0xf3, 0xa5, 0x7f, 0x11, 0x6a, 0x54, 0xcd, 0x67, 0xb9, 0x2e, 0x7d, 0x05,
+ 0xe3, 0x1c, 0x1d, 0xcc, 0x77, 0x8e, 0x97, 0xb1, 0xa0, 0x11, 0x09, 0x3d,
+ 0x90, 0x54, 0xfc, 0x7e, 0xbb, 0xbb, 0x21, 0x23, 0x03, 0x44, 0xbf, 0x7d,
+ 0x2c, 0xc9, 0x15, 0x42, 0xe5, 0xa0, 0x3b, 0xa2, 0xd1, 0x5b, 0x73, 0x81,
+ 0xff, 0xfa, 0x90, 0xfc, 0x27, 0x7b, 0x2f, 0x86, 0x9c, 0x1d, 0x14, 0x36,
+ 0x94, 0xa2, 0x6e, 0xe8, 0x9d, 0xa0, 0x5f, 0xfc, 0x5a, 0x0d, 0xa4, 0xd5,
+ 0x2f, 0x8d, 0xd6, 0x00, 0xfa, 0x93, 0x5b, 0x09, 0x7f, 0x42, 0x78, 0xcc,
+ 0x8c, 0x49, 0xda, 0xd9, 0xf6, 0x43, 0xe7, 0xe1, 0x3c, 0xa2, 0xe2, 0x70,
+ 0xe2, 0x6a, 0x99, 0xc5, 0xd6, 0xa2, 0xe3, 0x0b, 0xd4, 0x09, 0xac, 0x94,
+ 0xaf, 0xb7, 0xf0, 0xb3, 0x0c, 0x1e, 0xf5, 0x16, 0x4f, 0x53, 0x9a, 0xe3,
+ 0xcc, 0xe2, 0x0c, 0x4a, 0xb9, 0xe6, 0x06, 0xbb, 0xf7, 0x41, 0x43, 0x20,
+ 0x04, 0xee, 0x99, 0x2f, 0xd8, 0x9f, 0xda, 0x3f, 0xfd, 0x49, 0xb8, 0xc2,
+ 0xbd, 0xd9, 0xc5, 0x72, 0xfd, 0xe3, 0xce, 0x1c, 0xbc, 0xe4, 0x39, 0xac,
+ 0x2a, 0x99, 0xe9, 0xb4, 0x3e, 0x74, 0x10, 0xeb, 0xd5, 0x14, 0xcc, 0xdb,
+ 0xf1, 0x04, 0x63, 0x36, 0xfb, 0x1f, 0x2b, 0xe2, 0x73, 0xd4, 0xd8, 0x49,
+ 0x31, 0xa8, 0x55, 0xcc, 0xa7, 0x76, 0x36, 0x6e, 0x18, 0xdc, 0xb9, 0xb0,
+ 0x29, 0x99, 0xcf, 0x49, 0xbf, 0xf9, 0xdb, 0x7f, 0x24, 0x42, 0x02, 0xcb,
+ 0xc1, 0xaa, 0xcb, 0xba, 0x18, 0x85, 0x86, 0xc7, 0xf4, 0x1c, 0x62, 0x76,
+ 0xbc, 0x73, 0xfb, 0xe4, 0x15, 0xb8, 0xdd, 0x5d, 0xa6, 0x68, 0x39, 0xa5,
+ 0x3d, 0x33, 0xaf, 0xd5, 0x92, 0x4d, 0x48, 0xdb, 0x22, 0xc0, 0xdc, 0x49,
+ 0x5f, 0x7b, 0xa8, 0xd2, 0x62, 0x2d, 0xa7, 0x39, 0x93, 0x48, 0xe7, 0x6b,
+ 0x23, 0xba, 0xd4, 0xe0, 0xc1, 0x29, 0x55, 0xc4, 0x34, 0xe3, 0xac, 0x25,
+ 0xa7, 0x15, 0xad, 0xab, 0xb3, 0xb7, 0x25, 0xca, 0x37, 0x88, 0x40, 0x2e,
+ 0x47, 0x6e, 0x92, 0x20, 0x09, 0x2e, 0x5a, 0xec, 0xf2, 0xfb, 0xb3, 0xa0,
+ 0x16, 0xb6, 0x93, 0xf2, 0xf5, 0x8b, 0xfe, 0xaf, 0x25, 0xee, 0x2e, 0x98,
+ 0x6c, 0x0a, 0xfe, 0xae, 0x0b, 0x57, 0xf5, 0x9f, 0x3c, 0x80, 0xe9, 0x8b,
+ 0xaf, 0x92, 0x8a, 0xad, 0xe7, 0xa0, 0xe4, 0xe6, 0x0a, 0xa0, 0xc7, 0x83,
+ 0xb5, 0x48, 0x58, 0x5f, 0x55, 0x9e, 0x9b, 0x27, 0xcd, 0x31, 0x1f, 0x3e,
+ 0x50, 0x5a, 0x91, 0xad, 0x21, 0x1b, 0x97, 0x5b, 0xe8, 0xfa, 0x29, 0x8a,
+ 0xa4, 0x17, 0xe8, 0xab, 0x87, 0x02, 0xd6, 0x18, 0x8c, 0x9f, 0x65, 0xb7,
+ 0x2a, 0xfa, 0xde, 0x5f, 0x77, 0x30, 0x6c, 0x04, 0x22, 0xe6, 0x58, 0x26,
+ 0x14, 0x0d, 0x9c, 0x41, 0x0a, 0x82, 0x77, 0xdb, 0x40, 0xa1, 0x58, 0xac,
+ 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d, 0x31,
+ 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47, 0x72,
+ 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20,
+ 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x65,
+ 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
+ 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a,
+ 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30, 0x0b,
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77, 0x97,
+ 0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03, 0xdf,
+ 0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e, 0x77,
+ 0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52, 0x55,
+ 0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01, 0xc2,
+ 0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75, 0x88,
+ 0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1, 0xd1,
+ 0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d, 0xe3,
+ 0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e, 0x45,
+ 0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f, 0x86,
+ 0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae, 0xcd,
+ 0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5, 0x27,
+ 0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a, 0xf1,
+ 0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33, 0x1a,
+ 0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2, 0x8d,
+ 0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9, 0xc5,
+ 0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70, 0xa1,
+ 0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67, 0x04,
+ 0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a, 0x9a,
+ 0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3, 0x1c,
+ 0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33, 0xd6,
+ 0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4, 0xe9,
+ 0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1, 0x60,
+ 0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26, 0x83,
+ 0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41, 0xd7,
+ 0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda, 0x35,
+ 0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3, 0x0f,
+ 0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98, 0xa0,
+ 0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5, 0xaa,
+ 0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e, 0xe2,
+ 0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe, 0xc3,
+ 0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c, 0x24,
+ 0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6, 0xe6,
+ 0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5, 0x5e,
+ 0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f, 0x74,
+ 0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb, 0x3b,
+ 0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0, 0x8a,
+ 0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4, 0xdc,
+ 0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b, 0x83,
+ 0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52, 0x21,
+ 0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f, 0x51,
+ 0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c, 0x19,
+ 0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17, 0x35,
+ 0x23, 0x2d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x33, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69,
+ 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65,
+ 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a
+};
+unsigned int hi_double_len = 1374;
+
+unsigned char hi_double_extended[] = {
+ 0x68, 0x69, 0x0a, 0x30, 0x82, 0x05, 0x2f, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x05, 0x20, 0x30, 0x82,
+ 0x05, 0x1c, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
+ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x04,
+ 0xf9, 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x01, 0x30, 0x52, 0x30, 0x3a,
+ 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2f, 0x47,
+ 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74,
+ 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
+ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x02, 0x14,
+ 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, 0x91, 0x07,
+ 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, 0x0b, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x04, 0x82, 0x02, 0x00, 0x0e, 0xc2, 0x30, 0x38, 0x81, 0x23, 0x68, 0x90,
+ 0xae, 0x5f, 0xce, 0xf7, 0x27, 0xb1, 0x8c, 0x2e, 0x12, 0x10, 0xc6, 0x99,
+ 0xdc, 0x4d, 0x4b, 0x79, 0xda, 0xe4, 0x32, 0x10, 0x46, 0x1c, 0x16, 0x07,
+ 0x87, 0x66, 0x55, 0xff, 0x64, 0x1c, 0x61, 0x25, 0xd5, 0xb9, 0xe1, 0xfe,
+ 0xea, 0x5a, 0xcd, 0x56, 0xa5, 0xc3, 0xbe, 0xb1, 0x61, 0xc7, 0x6f, 0x5f,
+ 0x69, 0x20, 0x64, 0x50, 0x6f, 0x12, 0x78, 0xb6, 0x0c, 0x72, 0x44, 0x4f,
+ 0x60, 0x0f, 0x9f, 0xa2, 0x83, 0x3b, 0xc2, 0x83, 0xd5, 0x14, 0x1f, 0x6f,
+ 0x3e, 0xb2, 0x47, 0xb5, 0x58, 0xc5, 0xa7, 0xb4, 0x82, 0x53, 0x2e, 0x53,
+ 0x95, 0x4e, 0x3d, 0xe4, 0x62, 0xe8, 0xa1, 0xaf, 0xae, 0xbf, 0xa9, 0xd2,
+ 0x22, 0x07, 0xbe, 0x71, 0x37, 0x2c, 0x5a, 0xa7, 0x6c, 0xaf, 0x14, 0xc0,
+ 0x6c, 0x2f, 0xbf, 0x4f, 0x15, 0xc2, 0x0f, 0x8b, 0xdc, 0x68, 0x45, 0xdf,
+ 0xf3, 0xa5, 0x7f, 0x11, 0x6a, 0x54, 0xcd, 0x67, 0xb9, 0x2e, 0x7d, 0x05,
+ 0xe3, 0x1c, 0x1d, 0xcc, 0x77, 0x8e, 0x97, 0xb1, 0xa0, 0x11, 0x09, 0x3d,
+ 0x90, 0x54, 0xfc, 0x7e, 0xbb, 0xbb, 0x21, 0x23, 0x03, 0x44, 0xbf, 0x7d,
+ 0x2c, 0xc9, 0x15, 0x42, 0xe5, 0xa0, 0x3b, 0xa2, 0xd1, 0x5b, 0x73, 0x81,
+ 0xff, 0xfa, 0x90, 0xfc, 0x27, 0x7b, 0x2f, 0x86, 0x9c, 0x1d, 0x14, 0x36,
+ 0x94, 0xa2, 0x6e, 0xe8, 0x9d, 0xa0, 0x5f, 0xfc, 0x5a, 0x0d, 0xa4, 0xd5,
+ 0x2f, 0x8d, 0xd6, 0x00, 0xfa, 0x93, 0x5b, 0x09, 0x7f, 0x42, 0x78, 0xcc,
+ 0x8c, 0x49, 0xda, 0xd9, 0xf6, 0x43, 0xe7, 0xe1, 0x3c, 0xa2, 0xe2, 0x70,
+ 0xe2, 0x6a, 0x99, 0xc5, 0xd6, 0xa2, 0xe3, 0x0b, 0xd4, 0x09, 0xac, 0x94,
+ 0xaf, 0xb7, 0xf0, 0xb3, 0x0c, 0x1e, 0xf5, 0x16, 0x4f, 0x53, 0x9a, 0xe3,
+ 0xcc, 0xe2, 0x0c, 0x4a, 0xb9, 0xe6, 0x06, 0xbb, 0xf7, 0x41, 0x43, 0x20,
+ 0x04, 0xee, 0x99, 0x2f, 0xd8, 0x9f, 0xda, 0x3f, 0xfd, 0x49, 0xb8, 0xc2,
+ 0xbd, 0xd9, 0xc5, 0x72, 0xfd, 0xe3, 0xce, 0x1c, 0xbc, 0xe4, 0x39, 0xac,
+ 0x2a, 0x99, 0xe9, 0xb4, 0x3e, 0x74, 0x10, 0xeb, 0xd5, 0x14, 0xcc, 0xdb,
+ 0xf1, 0x04, 0x63, 0x36, 0xfb, 0x1f, 0x2b, 0xe2, 0x73, 0xd4, 0xd8, 0x49,
+ 0x31, 0xa8, 0x55, 0xcc, 0xa7, 0x76, 0x36, 0x6e, 0x18, 0xdc, 0xb9, 0xb0,
+ 0x29, 0x99, 0xcf, 0x49, 0xbf, 0xf9, 0xdb, 0x7f, 0x24, 0x42, 0x02, 0xcb,
+ 0xc1, 0xaa, 0xcb, 0xba, 0x18, 0x85, 0x86, 0xc7, 0xf4, 0x1c, 0x62, 0x76,
+ 0xbc, 0x73, 0xfb, 0xe4, 0x15, 0xb8, 0xdd, 0x5d, 0xa6, 0x68, 0x39, 0xa5,
+ 0x3d, 0x33, 0xaf, 0xd5, 0x92, 0x4d, 0x48, 0xdb, 0x22, 0xc0, 0xdc, 0x49,
+ 0x5f, 0x7b, 0xa8, 0xd2, 0x62, 0x2d, 0xa7, 0x39, 0x93, 0x48, 0xe7, 0x6b,
+ 0x23, 0xba, 0xd4, 0xe0, 0xc1, 0x29, 0x55, 0xc4, 0x34, 0xe3, 0xac, 0x25,
+ 0xa7, 0x15, 0xad, 0xab, 0xb3, 0xb7, 0x25, 0xca, 0x37, 0x88, 0x40, 0x2e,
+ 0x47, 0x6e, 0x92, 0x20, 0x09, 0x2e, 0x5a, 0xec, 0xf2, 0xfb, 0xb3, 0xa0,
+ 0x16, 0xb6, 0x93, 0xf2, 0xf5, 0x8b, 0xfe, 0xaf, 0x25, 0xee, 0x2e, 0x98,
+ 0x6c, 0x0a, 0xfe, 0xae, 0x0b, 0x57, 0xf5, 0x9f, 0x3c, 0x80, 0xe9, 0x8b,
+ 0xaf, 0x92, 0x8a, 0xad, 0xe7, 0xa0, 0xe4, 0xe6, 0x0a, 0xa0, 0xc7, 0x83,
+ 0xb5, 0x48, 0x58, 0x5f, 0x55, 0x9e, 0x9b, 0x27, 0xcd, 0x31, 0x1f, 0x3e,
+ 0x50, 0x5a, 0x91, 0xad, 0x21, 0x1b, 0x97, 0x5b, 0xe8, 0xfa, 0x29, 0x8a,
+ 0xa4, 0x17, 0xe8, 0xab, 0x87, 0x02, 0xd6, 0x18, 0x8c, 0x9f, 0x65, 0xb7,
+ 0x2a, 0xfa, 0xde, 0x5f, 0x77, 0x30, 0x6c, 0x04, 0x22, 0xe6, 0x58, 0x26,
+ 0x14, 0x0d, 0x9c, 0x41, 0x0a, 0x82, 0x77, 0xdb, 0x40, 0xa1, 0x58, 0xac,
+ 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d, 0x31,
+ 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47, 0x72,
+ 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20,
+ 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x65,
+ 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
+ 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a,
+ 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30, 0x0b,
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77, 0x97,
+ 0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03, 0xdf,
+ 0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e, 0x77,
+ 0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52, 0x55,
+ 0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01, 0xc2,
+ 0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75, 0x88,
+ 0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1, 0xd1,
+ 0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d, 0xe3,
+ 0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e, 0x45,
+ 0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f, 0x86,
+ 0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae, 0xcd,
+ 0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5, 0x27,
+ 0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a, 0xf1,
+ 0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33, 0x1a,
+ 0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2, 0x8d,
+ 0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9, 0xc5,
+ 0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70, 0xa1,
+ 0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67, 0x04,
+ 0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a, 0x9a,
+ 0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3, 0x1c,
+ 0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33, 0xd6,
+ 0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4, 0xe9,
+ 0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1, 0x60,
+ 0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26, 0x83,
+ 0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41, 0xd7,
+ 0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda, 0x35,
+ 0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3, 0x0f,
+ 0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98, 0xa0,
+ 0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5, 0xaa,
+ 0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e, 0xe2,
+ 0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe, 0xc3,
+ 0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c, 0x24,
+ 0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6, 0xe6,
+ 0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5, 0x5e,
+ 0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f, 0x74,
+ 0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb, 0x3b,
+ 0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0, 0x8a,
+ 0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4, 0xdc,
+ 0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b, 0x83,
+ 0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52, 0x21,
+ 0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f, 0x51,
+ 0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c, 0x19,
+ 0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17, 0x35,
+ 0x23, 0x2d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x05, 0x34, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73,
+ 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70,
+ 0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a
+};
+unsigned int hi_double_extended_len = 1375;
+
+unsigned char certificate_printable_der[] = {
+ 0x30, 0x82, 0x03, 0x39, 0x30, 0x82, 0x02, 0x21, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x09, 0x00, 0xde, 0xf6, 0x22, 0xc4, 0xf2, 0xf1, 0x86, 0x02,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0b, 0x05, 0x00, 0x30, 0x2a, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x1f, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20,
+ 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20,
+ 0x43, 0x41, 0x20, 0x32, 0x20, 0x28, 0x62, 0x65, 0x74, 0x61, 0x29, 0x30,
+ 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x31, 0x30, 0x33, 0x31, 0x31, 0x34, 0x31,
+ 0x39, 0x32, 0x33, 0x5a, 0x17, 0x0d, 0x33, 0x37, 0x31, 0x30, 0x32, 0x35,
+ 0x31, 0x34, 0x31, 0x39, 0x32, 0x33, 0x5a, 0x30, 0x2f, 0x31, 0x2d, 0x30,
+ 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, 0x52, 0x65, 0x64, 0x20,
+ 0x48, 0x61, 0x74, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42,
+ 0x6f, 0x6f, 0x74, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20,
+ 0x33, 0x20, 0x28, 0x62, 0x65, 0x74, 0x61, 0x29, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xbd, 0xda, 0xa1, 0xed, 0x8d, 0x8e, 0x15,
+ 0x5c, 0xf8, 0x01, 0x77, 0x48, 0x4a, 0x60, 0x96, 0xf9, 0x27, 0xfa, 0xe2,
+ 0xb1, 0x69, 0x0f, 0x51, 0x19, 0x52, 0x7e, 0xc4, 0x34, 0x8e, 0xe1, 0x9b,
+ 0x9c, 0xa4, 0xb1, 0x5c, 0xd6, 0x81, 0x98, 0x78, 0xfe, 0xa9, 0xe5, 0x0b,
+ 0x00, 0xba, 0x9c, 0x64, 0x7e, 0xc7, 0xcc, 0x72, 0xb1, 0x73, 0x4b, 0x11,
+ 0x07, 0x52, 0xf0, 0x20, 0x96, 0x8b, 0x99, 0x39, 0xde, 0xdb, 0xfa, 0x3d,
+ 0x45, 0xe2, 0x98, 0x7b, 0x0c, 0x41, 0xe4, 0x0c, 0xb5, 0x5d, 0x92, 0x74,
+ 0x39, 0x96, 0xe1, 0x97, 0x97, 0xa1, 0xad, 0x2e, 0xcc, 0xd0, 0x1b, 0x4d,
+ 0x9d, 0xbd, 0x3e, 0xa9, 0x36, 0x8e, 0xcc, 0xc7, 0x5f, 0x6a, 0x7d, 0x39,
+ 0x5e, 0x0b, 0x8d, 0xca, 0xe4, 0x83, 0xe9, 0x3b, 0x5c, 0x86, 0x47, 0xd4,
+ 0xba, 0x7d, 0x98, 0x26, 0xa1, 0xf4, 0xe8, 0x90, 0x6b, 0x0f, 0xf1, 0x6b,
+ 0x8c, 0xe3, 0xa2, 0x80, 0x3c, 0x96, 0xf1, 0x0a, 0xb6, 0x66, 0xc0, 0x4b,
+ 0x61, 0xf7, 0x74, 0xcd, 0xd3, 0x7b, 0x8e, 0x5e, 0x39, 0xda, 0x99, 0x20,
+ 0x33, 0x93, 0xd3, 0xf0, 0x7f, 0xad, 0x35, 0xe9, 0x88, 0x8d, 0x9c, 0xbf,
+ 0x65, 0xf1, 0x47, 0x02, 0xf9, 0x7c, 0xed, 0x27, 0x5f, 0x4a, 0x65, 0x3c,
+ 0xcf, 0x5f, 0x0e, 0x88, 0x95, 0x74, 0xde, 0xfb, 0x9e, 0x2e, 0x91, 0x9b,
+ 0x45, 0x37, 0xc8, 0x85, 0xff, 0xe3, 0x41, 0x70, 0xfe, 0xd5, 0xef, 0x0e,
+ 0x82, 0x22, 0x08, 0xb7, 0x3b, 0x44, 0x3e, 0xdc, 0x5b, 0x7f, 0xba, 0xbf,
+ 0xe6, 0x58, 0x9d, 0x02, 0x6e, 0x75, 0xbf, 0x50, 0xec, 0xcf, 0x3f, 0xa5,
+ 0x91, 0x0a, 0xe2, 0x59, 0x2c, 0xc3, 0xe7, 0x05, 0x03, 0xe8, 0xf2, 0x6f,
+ 0x2a, 0x04, 0x68, 0x9a, 0x31, 0x32, 0x8f, 0x04, 0x35, 0xcd, 0x1f, 0x34,
+ 0xcc, 0x4f, 0x79, 0x5a, 0x99, 0x8d, 0x9d, 0x5c, 0xf5, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x1d, 0x06,
+ 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x65, 0xc5, 0xbe, 0xca,
+ 0xe6, 0x59, 0x6a, 0xfd, 0x6c, 0x71, 0xc4, 0xa7, 0x98, 0xc6, 0x25, 0x8d,
+ 0x7b, 0x67, 0x05, 0xd0, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0x81, 0xf8, 0xee, 0x47, 0x5c, 0x3e, 0xed,
+ 0xfb, 0xce, 0xa5, 0x84, 0xbe, 0xd7, 0xae, 0xdb, 0xd3, 0x7d, 0x64, 0xb3,
+ 0x2a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x66, 0x1e, 0x3d,
+ 0x1d, 0x53, 0x33, 0xde, 0x4e, 0xc7, 0xc4, 0xf4, 0xdf, 0xda, 0x18, 0x19,
+ 0x8a, 0xa9, 0xff, 0xe2, 0x63, 0x2b, 0xbe, 0xf2, 0x61, 0x63, 0xe2, 0xf6,
+ 0xed, 0x47, 0x1a, 0x71, 0x02, 0xec, 0x2a, 0xef, 0x89, 0x77, 0xe3, 0xfd,
+ 0x86, 0x69, 0xf1, 0x3f, 0x0d, 0xf9, 0x6e, 0xf9, 0x3b, 0xad, 0x26, 0x47,
+ 0xb7, 0xf2, 0x0d, 0xad, 0x23, 0xa3, 0x67, 0x3b, 0xcb, 0x6d, 0x9e, 0x03,
+ 0x0f, 0xbc, 0x69, 0x73, 0x9f, 0xd4, 0xa5, 0x0f, 0x6f, 0xf8, 0xab, 0x4d,
+ 0x36, 0xd1, 0xe0, 0xe0, 0x5d, 0x20, 0x43, 0x90, 0xc4, 0x65, 0x61, 0x93,
+ 0xe2, 0x0f, 0x51, 0x59, 0x0a, 0xf7, 0x88, 0x70, 0x57, 0xb9, 0x04, 0xa9,
+ 0x32, 0x57, 0x9c, 0xb3, 0x57, 0x38, 0x8b, 0x8e, 0x46, 0xc8, 0x32, 0x6c,
+ 0xb4, 0xf3, 0x96, 0x7f, 0x4b, 0xf0, 0x88, 0xf9, 0x7f, 0xe2, 0x71, 0xe1,
+ 0x8b, 0xe2, 0x14, 0xf1, 0x4b, 0x25, 0x00, 0x48, 0x1c, 0x7e, 0xe5, 0x8d,
+ 0x65, 0x2d, 0xeb, 0x72, 0x4f, 0x92, 0x44, 0xf3, 0xe6, 0xe0, 0xd0, 0xdf,
+ 0x85, 0xa8, 0x13, 0x4a, 0xfb, 0x99, 0xca, 0x14, 0x2c, 0x97, 0x80, 0x93,
+ 0x27, 0xd3, 0x20, 0xf8, 0x6d, 0x29, 0x28, 0x2c, 0xb9, 0x77, 0xea, 0xb1,
+ 0x63, 0xbd, 0x7d, 0x53, 0xfd, 0x4a, 0x62, 0x64, 0x0b, 0x98, 0xa8, 0xae,
+ 0x11, 0xfc, 0x6e, 0x8d, 0x63, 0xd4, 0x15, 0x55, 0xc6, 0x4c, 0x74, 0xf5,
+ 0x5f, 0xa0, 0xb9, 0x2c, 0x2d, 0x9a, 0x7a, 0x87, 0x6e, 0xf0, 0x5e, 0x25,
+ 0xed, 0xfc, 0xd8, 0xc4, 0x34, 0x33, 0x32, 0xad, 0x01, 0xd4, 0x4b, 0x49,
+ 0x51, 0xc2, 0x07, 0x7f, 0x90, 0x6d, 0xea, 0xf5, 0x4c, 0x41, 0x71, 0x64,
+ 0xeb, 0x1f, 0x29, 0xa3, 0x1f, 0x64, 0xa2, 0x1e, 0x0e, 0x6f, 0xa1, 0x67,
+ 0x99, 0x8d, 0x98, 0x1c, 0xb8, 0x53, 0x9d, 0x30, 0x1d, 0xae, 0x32, 0x56,
+ 0xd2
+};
+unsigned int certificate_printable_der_len = 829;
+
+unsigned char certificate_eku_der[] = {
+ 0x30, 0x82, 0x03, 0x90, 0x30, 0x82, 0x02, 0x78, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x09, 0x00, 0xd3, 0x9c, 0x41, 0x33, 0xdd, 0x6b, 0x5f, 0x45,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0b, 0x05, 0x00, 0x30, 0x47, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x0c, 0x18, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20,
+ 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20,
+ 0x43, 0x41, 0x20, 0x36, 0x31, 0x22, 0x30, 0x20, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x13, 0x73, 0x65, 0x63,
+ 0x61, 0x6c, 0x65, 0x72, 0x74, 0x40, 0x72, 0x65, 0x64, 0x68, 0x61, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x32,
+ 0x31, 0x35, 0x31, 0x34, 0x30, 0x30, 0x34, 0x34, 0x5a, 0x17, 0x0d, 0x33,
+ 0x38, 0x30, 0x31, 0x31, 0x37, 0x31, 0x34, 0x30, 0x30, 0x34, 0x34, 0x5a,
+ 0x30, 0x4e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
+ 0x1f, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20, 0x53, 0x65, 0x63,
+ 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20, 0x53, 0x69, 0x67,
+ 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x36, 0x30, 0x32, 0x31, 0x22, 0x30, 0x20,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16,
+ 0x13, 0x73, 0x65, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x40, 0x72, 0x65,
+ 0x64, 0x68, 0x61, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xaa, 0x6f, 0xbb, 0x92, 0x77, 0xd7, 0x15,
+ 0xef, 0x88, 0x80, 0x88, 0xc0, 0xe7, 0x89, 0xeb, 0x35, 0x76, 0xf4, 0x85,
+ 0x05, 0x0f, 0x19, 0xe4, 0x5f, 0x25, 0xdd, 0xc1, 0xa2, 0xe5, 0x5c, 0x06,
+ 0xfb, 0xf1, 0x06, 0xb5, 0x65, 0x45, 0xcb, 0xbd, 0x19, 0x33, 0x54, 0xb5,
+ 0x1a, 0xcd, 0xe4, 0xa8, 0x35, 0x2a, 0xfe, 0x9c, 0x53, 0xf4, 0xc6, 0x76,
+ 0xdb, 0x1f, 0x8a, 0xd4, 0x7b, 0x18, 0x11, 0xaf, 0xa3, 0x90, 0xd4, 0xdd,
+ 0x4d, 0xd5, 0x42, 0xcc, 0x14, 0x9a, 0x64, 0x6b, 0xc0, 0x7f, 0xaa, 0x1c,
+ 0x94, 0x47, 0x4d, 0x79, 0xbd, 0x57, 0x9a, 0xbf, 0x99, 0x4e, 0x96, 0xa9,
+ 0x31, 0x2c, 0xa9, 0xe7, 0x14, 0x65, 0x86, 0xc8, 0xac, 0x79, 0x5e, 0x78,
+ 0xa4, 0x3c, 0x00, 0x24, 0xd3, 0xf7, 0xe1, 0xf5, 0x12, 0xad, 0xa0, 0x29,
+ 0xe5, 0xfe, 0x80, 0xae, 0xf8, 0xaa, 0x60, 0x36, 0xe7, 0xe8, 0x94, 0xcb,
+ 0xe9, 0xd1, 0xcc, 0x0b, 0x4d, 0xf7, 0xde, 0xeb, 0x52, 0xd2, 0x73, 0x09,
+ 0x28, 0xdf, 0x48, 0x99, 0x53, 0x9f, 0xc5, 0x9a, 0xd4, 0x36, 0xa3, 0xc6,
+ 0x5e, 0x8d, 0xbe, 0xd5, 0xdc, 0x76, 0xb4, 0x74, 0xb8, 0x26, 0x18, 0x27,
+ 0xfb, 0xf2, 0xfb, 0xd0, 0x9b, 0x3d, 0x7f, 0x10, 0xe2, 0xab, 0x44, 0xc7,
+ 0x88, 0x7f, 0xb4, 0x3d, 0x3e, 0xa3, 0xff, 0x6d, 0x06, 0x4b, 0x3e, 0x55,
+ 0xb2, 0x84, 0xf4, 0xad, 0x54, 0x88, 0x81, 0xc3, 0x9c, 0xf8, 0xb6, 0x68,
+ 0x96, 0x38, 0x8b, 0xcd, 0x90, 0x6d, 0x25, 0x4b, 0xbf, 0x0c, 0x44, 0x90,
+ 0xa5, 0x5b, 0x98, 0xd0, 0x40, 0x2f, 0xbb, 0x0d, 0xa8, 0x4b, 0x8a, 0x62,
+ 0x82, 0x46, 0x46, 0x18, 0x38, 0xae, 0x82, 0x07, 0xd0, 0xb4, 0x2f, 0x16,
+ 0x79, 0x55, 0x9f, 0x1b, 0xc5, 0x08, 0x6d, 0x85, 0xdf, 0x3f, 0xa9, 0x9b,
+ 0x4b, 0xc6, 0x28, 0xd3, 0x58, 0x72, 0x3d, 0x37, 0x11, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x78, 0x30, 0x76, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80,
+ 0x30, 0x16, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x01, 0x01, 0xff, 0x04, 0x0c,
+ 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03,
+ 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x6c,
+ 0xe4, 0x6c, 0x27, 0xaa, 0xcd, 0x0d, 0x4b, 0x74, 0x21, 0xa4, 0xf6, 0x5f,
+ 0x87, 0xb5, 0x31, 0xfe, 0x10, 0xbb, 0xa7, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe8, 0x6a, 0x1c, 0xab,
+ 0x2c, 0x48, 0xf9, 0x60, 0x36, 0xa2, 0xf0, 0x7b, 0x8e, 0xd2, 0x9d, 0xb4,
+ 0x2a, 0x28, 0x98, 0xc8, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+ 0x55, 0x34, 0xe2, 0xfa, 0xf6, 0x89, 0x86, 0xad, 0x92, 0x21, 0xec, 0xb9,
+ 0x54, 0x0e, 0x18, 0x47, 0x0d, 0x1b, 0xa7, 0x58, 0xad, 0x69, 0xe4, 0xef,
+ 0x3b, 0xe6, 0x8d, 0xdd, 0xda, 0x0c, 0x45, 0xf6, 0xe8, 0x96, 0xa4, 0x29,
+ 0x0f, 0xbb, 0xcf, 0x16, 0xae, 0x93, 0xd0, 0xcb, 0x2a, 0x26, 0x1a, 0x7b,
+ 0xfc, 0x51, 0x22, 0x76, 0x98, 0x31, 0xa7, 0x0f, 0x29, 0x35, 0x79, 0xbf,
+ 0xe2, 0x4f, 0x0f, 0x14, 0xf5, 0x1f, 0xcb, 0xbf, 0x87, 0x65, 0x13, 0x32,
+ 0xa3, 0x19, 0x4a, 0xd1, 0x3f, 0x45, 0xd4, 0x4b, 0xe2, 0x00, 0x26, 0xa9,
+ 0x3e, 0xd7, 0xa5, 0x37, 0x9f, 0xf5, 0xad, 0x61, 0xe2, 0x40, 0xa9, 0x74,
+ 0x24, 0x53, 0xf2, 0x78, 0xeb, 0x10, 0x9b, 0x2c, 0x27, 0x88, 0x46, 0xcb,
+ 0xe4, 0x60, 0xca, 0xf5, 0x06, 0x24, 0x40, 0x2a, 0x97, 0x3a, 0xcc, 0xd0,
+ 0x81, 0xb1, 0x15, 0xa3, 0x4f, 0xd0, 0x2b, 0x4f, 0xca, 0x6e, 0xaa, 0x24,
+ 0x31, 0xb3, 0xac, 0xa6, 0x75, 0x05, 0xfe, 0x8a, 0xf4, 0x41, 0xc4, 0x06,
+ 0x8a, 0xc7, 0x0a, 0x83, 0x4e, 0x49, 0xd4, 0x3f, 0x83, 0x50, 0xec, 0x57,
+ 0x04, 0x97, 0x14, 0x49, 0xf5, 0xe1, 0xb1, 0x7a, 0x9c, 0x09, 0x4f, 0x61,
+ 0x87, 0xc3, 0x97, 0x22, 0x17, 0xc2, 0xeb, 0xcc, 0x32, 0x81, 0x31, 0x21,
+ 0x3f, 0x10, 0x57, 0x5b, 0x43, 0xbe, 0xcd, 0x68, 0x82, 0xbe, 0xe5, 0xc1,
+ 0x65, 0x94, 0x7e, 0xc2, 0x34, 0x76, 0x2b, 0xcf, 0x89, 0x3c, 0x2b, 0x81,
+ 0x23, 0x72, 0x95, 0xcf, 0xc9, 0x67, 0x19, 0x2a, 0xd5, 0x5c, 0xca, 0xa3,
+ 0x46, 0xbd, 0x48, 0x06, 0x0b, 0xa6, 0xa3, 0x96, 0x50, 0x28, 0xc7, 0x7e,
+ 0xcf, 0x62, 0xf2, 0xfa, 0xc4, 0xf2, 0x53, 0xe3, 0xc9, 0xe8, 0x2e, 0xdd,
+ 0x29, 0x37, 0x07, 0x47, 0xff, 0xff, 0x8a, 0x32, 0xbd, 0xa2, 0xb7, 0x21,
+ 0x89, 0xa0, 0x55, 0xf7
+};
+unsigned int certificate_eku_der_len = 916;
diff --git a/grub-core/tests/argon2_test.c b/grub-core/tests/argon2_test.c
new file mode 100644
index 000000000..8318a0962
--- /dev/null
+++ b/grub-core/tests/argon2_test.c
@@ -0,0 +1,139 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2025 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 .
+ */
+
+#include
+#include
+#include
+#include
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define DIM(v) (sizeof(v)/sizeof((v)[0]))
+
+static void
+argon2_test (void)
+{
+ gcry_error_t err;
+ static struct {
+ int subalgo;
+ unsigned long param[4];
+ grub_size_t passlen;
+ const char *pass;
+ grub_size_t saltlen;
+ const char *salt;
+ grub_size_t keylen;
+ const char *key;
+ grub_size_t adlen;
+ const char *ad;
+ grub_size_t dklen;
+ const char *dk;
+ } tv[] = {
+ {
+ GRUB_GCRY_KDF_ARGON2D,
+ { 32, 3, 32, 4 },
+ 32,
+ "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01",
+ 16,
+ "\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02",
+ 8,
+ "\x03\x03\x03\x03\x03\x03\x03\x03",
+ 12,
+ "\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04",
+ 32,
+ "\x51\x2b\x39\x1b\x6f\x11\x62\x97\x53\x71\xd3\x09\x19\x73\x42\x94"
+ "\xf8\x68\xe3\xbe\x39\x84\xf3\xc1\xa1\x3a\x4d\xb9\xfa\xbe\x4a\xcb"
+ },
+ {
+ GRUB_GCRY_KDF_ARGON2I,
+ { 32, 3, 32, 4 },
+ 32,
+ "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01",
+ 16,
+ "\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02",
+ 8,
+ "\x03\x03\x03\x03\x03\x03\x03\x03",
+ 12,
+ "\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04",
+ 32,
+ "\xc8\x14\xd9\xd1\xdc\x7f\x37\xaa\x13\xf0\xd7\x7f\x24\x94\xbd\xa1"
+ "\xc8\xde\x6b\x01\x6d\xd3\x88\xd2\x99\x52\xa4\xc4\x67\x2b\x6c\xe8"
+ },
+ {
+ GRUB_GCRY_KDF_ARGON2ID,
+ { 32, 3, 32, 4 },
+ 32,
+ "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01",
+ 16,
+ "\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02",
+ 8,
+ "\x03\x03\x03\x03\x03\x03\x03\x03",
+ 12,
+ "\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04",
+ 32,
+ "\x0d\x64\x0d\xf5\x8d\x78\x76\x6c\x08\xc0\x37\xa3\x4a\x8b\x53\xc9"
+ "\xd0\x1e\xf0\x45\x2d\x75\xb6\x5e\xb5\x25\x20\xe9\x6b\x01\xe6\x59"
+ },
+ {
+ /* empty password */
+ GRUB_GCRY_KDF_ARGON2I,
+ { 32, 3, 128, 1 },
+ 0, NULL,
+ 16,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+ 0, NULL,
+ 0, NULL,
+ 32,
+ "\xbb\x1f\xf2\xb9\x9f\xd4\x4a\xd9\xdf\x7f\xb9\x54\x55\x9e\xb8\xeb"
+ "\xb5\x9d\xab\xce\x2e\x62\x9f\x9b\x89\x09\xfe\xde\x57\xcc\x63\x86"
+ },
+ {
+ /* empty password */
+ GRUB_GCRY_KDF_ARGON2ID,
+ { 32, 3, 128, 1 },
+ 0, NULL,
+ 16,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+ 0, NULL,
+ 0, NULL,
+ 32,
+ "\x09\x2f\x38\x35\xac\xb2\x43\x92\x93\xeb\xcd\xe8\x04\x16\x6a\x31"
+ "\xce\x14\xd4\x55\xdb\xd8\xf7\xe6\xb4\xf5\x9d\x64\x8e\xd0\x3a\xdb"
+ },
+ };
+ unsigned char out[32];
+ unsigned int count;
+
+ for (count = 0; count < DIM(tv); count++)
+ {
+ err = grub_crypto_argon2 (tv[count].subalgo,
+ tv[count].param, 4,
+ tv[count].pass, tv[count].passlen,
+ tv[count].salt, tv[count].saltlen,
+ tv[count].key, tv[count].keylen,
+ tv[count].ad, tv[count].adlen,
+ tv[count].dklen, out);
+ grub_test_assert (err == 0, "argon2 test %d failed: %d", count, err);
+ grub_test_assert (grub_memcmp (out, tv[count].dk, tv[count].dklen) == 0,
+ "argon2 test %d failed: mismatch", count);
+ }
+}
+
+GRUB_FUNCTIONAL_TEST (argon2_test, argon2_test);
diff --git a/grub-core/tests/crypto_cipher_mode_test.c b/grub-core/tests/crypto_cipher_mode_test.c
new file mode 100644
index 000000000..f0ebcf377
--- /dev/null
+++ b/grub-core/tests/crypto_cipher_mode_test.c
@@ -0,0 +1,197 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2025 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 .
+ */
+
+#include
+#include
+#include
+#include
+
+#include "crypto_cipher_mode_vectors.h"
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+/* Perform cipher lookup, handle init, and key setting. */
+static grub_crypto_cipher_handle_t
+handle_init (struct vector vec, grub_crypto_cipher_handle_t handle)
+{
+ gcry_err_code_t err;
+
+ const gcry_cipher_spec_t *cipher = grub_crypto_lookup_cipher_by_name (vec.cipher);
+ grub_test_assert (cipher != NULL, "\n%s: cipher lookup failed for %s", vec.mode, vec.cipher);
+ if (cipher == NULL)
+ return NULL;
+
+ handle = grub_crypto_cipher_open (cipher);
+ grub_test_assert (handle != NULL, "\n%s: handle init failed for %s", vec.mode, vec.cipher);
+ if (handle == NULL)
+ return NULL;
+
+ err = grub_crypto_cipher_set_key (handle, (grub_uint8_t *) vec.key, vec.keylen);
+ grub_test_assert (err == GPG_ERR_NO_ERROR, "\n%s: key set of size %d failed for %s with err = %d",
+ vec.mode, vec.keylen, vec.cipher, err);
+ if (err != GPG_ERR_NO_ERROR)
+ {
+ grub_crypto_cipher_close (handle);
+ return NULL;
+ }
+
+ return handle;
+}
+
+static void
+ecb_test (struct vector vec)
+{
+ gcry_err_code_t gcry_err;
+ grub_crypto_cipher_handle_t handle = NULL;
+ grub_uint8_t *plaintext = NULL, *ciphertext = NULL;
+ grub_int32_t rc;
+
+ handle = handle_init (vec, handle);
+ if (handle == NULL)
+ return;
+
+ /* Test encryption. */
+ ciphertext = grub_zalloc (vec.plen);
+ grub_test_assert (ciphertext != NULL, "\necb: ciphertext buffer allocation failed");
+ if (ciphertext == NULL)
+ goto out_handle;
+
+ gcry_err = grub_crypto_ecb_encrypt (handle, ciphertext, vec.ptext, vec.plen);
+ grub_test_assert (gcry_err == GPG_ERR_NO_ERROR, "\necb: encryption failed with err = %d",
+ gcry_err);
+ if (gcry_err != GPG_ERR_NO_ERROR)
+ goto out_ct;
+
+ rc = grub_memcmp (ciphertext, vec.ctext, vec.plen);
+ grub_test_assert (rc == 0, "\necb: ciphertext mismatch after encryption");
+ if (rc != 0)
+ goto out_ct;
+
+ /* Test decryption. */
+ plaintext = grub_zalloc (vec.plen);
+ grub_test_assert (plaintext != NULL, "\necb: plaintext buffer allocation failed");
+ if (plaintext == NULL)
+ goto out_ct;
+
+ gcry_err = grub_crypto_ecb_decrypt (handle, plaintext, ciphertext, vec.plen);
+ grub_test_assert (gcry_err == GPG_ERR_NO_ERROR, "\necb: decryption failed failed with err = %d",
+ gcry_err);
+ if (gcry_err != GPG_ERR_NO_ERROR)
+ goto out_pt;
+
+ rc = grub_memcmp (plaintext, vec.ptext, vec.plen);
+ grub_test_assert (rc == 0, "\necb: plaintext mismatch after decryption");
+
+ out_pt:
+ grub_free(plaintext);
+ out_ct:
+ grub_free(ciphertext);
+ out_handle:
+ grub_crypto_cipher_close(handle);
+}
+
+static void
+cbc_test (struct vector vec)
+{
+ gcry_err_code_t gcry_err;
+ grub_crypto_cipher_handle_t handle = NULL;
+ grub_uint8_t *plaintext = NULL, *ciphertext = NULL;
+ grub_uint32_t *iv = NULL;
+ grub_int32_t rc;
+
+ handle = handle_init (vec, handle);
+ if (handle == NULL)
+ return;
+
+ /* Test Encryption */
+ iv = grub_malloc(vec.ivlen);
+ grub_test_assert (iv != NULL, "\ncbc: IV buffer allocation failed");
+ if (iv == NULL)
+ goto out_handle;
+
+ grub_memcpy (iv, vec.iv_in, vec.ivlen);
+
+ ciphertext = grub_zalloc (vec.plen);
+ grub_test_assert (ciphertext != NULL, "\ncbc: ciphertext buffer allocation failed");
+ if (ciphertext == NULL)
+ goto out_iv;
+
+ gcry_err = grub_crypto_cbc_encrypt (handle, ciphertext, vec.ptext, vec.plen, iv);
+ grub_test_assert (gcry_err == GPG_ERR_NO_ERROR, "\ncbc: encryption failed with err = %d",
+ gcry_err);
+ if (gcry_err != GPG_ERR_NO_ERROR)
+ goto out_ct;
+
+ rc = grub_memcmp (ciphertext, vec.ctext, vec.plen);
+ grub_test_assert (rc == 0, "\ncbc: ciphertext mismatch after encryption");
+ if (rc != 0)
+ goto out_ct;
+
+ rc = grub_memcmp (iv, vec.iv_out, vec.ivlen);
+ grub_test_assert (rc == 0, "\ncbc: IV out mismatch after encryption");
+ if (rc != 0)
+ goto out_ct;
+
+ /* Test Decryption */
+ grub_memcpy (iv, vec.iv_in, vec.ivlen);
+
+ plaintext = grub_zalloc (vec.plen);
+ grub_test_assert (plaintext != NULL, "\ncbc: plaintext buffer allocation failed");
+ if (plaintext == NULL)
+ goto out_ct;
+
+ gcry_err = grub_crypto_cbc_decrypt (handle, plaintext, ciphertext, vec.plen, iv);
+ grub_test_assert (gcry_err == GPG_ERR_NO_ERROR, "\ncbc: decryption failed with err = %d",
+ gcry_err);
+ if (gcry_err != GPG_ERR_NO_ERROR)
+ goto out_pt;
+
+ rc = grub_memcmp (plaintext, vec.ptext, vec.plen);
+ grub_test_assert (rc == 0, "\ncbc: plaintext mismatch after decryption");
+
+ out_pt:
+ grub_free(plaintext);
+ out_ct:
+ grub_free(ciphertext);
+ out_iv:
+ grub_free(iv);
+ out_handle:
+ grub_crypto_cipher_close(handle);
+}
+
+static void
+crypto_cipher_mode_test (void)
+{
+ grub_size_t i;
+
+ for (i = 0; i < ARRAY_SIZE (vecs); i++)
+ {
+ if (grub_strcmp (vecs[i].mode, "ecb") == 0)
+ ecb_test(vecs[i]);
+ else if (grub_strcmp (vecs[i].mode, "cbc") == 0)
+ cbc_test(vecs[i]);
+ else
+ {
+ grub_test_assert(0, "\n%s mode unsupported for testing", vecs[i].mode);
+ return;
+ }
+ }
+}
+
+/* Register example_test method as a functional test. */
+GRUB_FUNCTIONAL_TEST (crypto_cipher_mode_test, crypto_cipher_mode_test);
diff --git a/grub-core/tests/crypto_cipher_mode_vectors.h b/grub-core/tests/crypto_cipher_mode_vectors.h
new file mode 100644
index 000000000..8ef948b46
--- /dev/null
+++ b/grub-core/tests/crypto_cipher_mode_vectors.h
@@ -0,0 +1,135 @@
+struct vector
+{
+ const char *cipher;
+ const char *mode;
+ const char *key;
+ grub_uint32_t keylen;
+ const char *ptext;
+ grub_uint32_t plen;
+ const char *ctext;
+ const char *iv_in;
+ const char *iv_out;
+ grub_uint32_t ivlen;
+} vecs[] = {
+ {
+ .cipher = "aes",
+ .mode = "ecb",
+ .key = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+ .keylen = 16,
+ .ptext = "\x00\x11\x22\x33\x44\x55\x66\x77"
+ "\x88\x99\xaa\xbb\xcc\xdd\xee\xff",
+ .plen = 16,
+ .ctext = "\x69\xc4\xe0\xd8\x6a\x7b\x04\x30"
+ "\xd8\xcd\xb7\x80\x70\xb4\xc5\x5a",
+ },
+ {
+ .cipher = "aes",
+ .mode = "ecb",
+ .key = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+ "\x10\x11\x12\x13\x14\x15\x16\x17",
+ .keylen = 24,
+ .ptext = "\x00\x11\x22\x33\x44\x55\x66\x77"
+ "\x88\x99\xaa\xbb\xcc\xdd\xee\xff",
+ .plen = 16,
+ .ctext = "\xdd\xa9\x7c\xa4\x86\x4c\xdf\xe0"
+ "\x6e\xaf\x70\xa0\xec\x0d\x71\x91",
+ },
+ {
+ .cipher = "aes",
+ .mode = "ecb",
+ .key = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+ "\x10\x11\x12\x13\x14\x15\x16\x17"
+ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
+ .keylen = 32,
+ .ptext = "\x00\x11\x22\x33\x44\x55\x66\x77"
+ "\x88\x99\xaa\xbb\xcc\xdd\xee\xff",
+ .plen = 16,
+ .ctext = "\x8e\xa2\xb7\xca\x51\x67\x45\xbf"
+ "\xea\xfc\x49\x90\x4b\x49\x60\x89",
+ },
+ {
+ .cipher = "aes",
+ .mode = "cbc",
+ .key = "\xc2\x86\x69\x6d\x88\x7c\x9a\xa0"
+ "\x61\x1b\xbb\x3e\x20\x25\xa4\x5a",
+ .keylen = 16,
+ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+ "\x10\x11\x12\x13\x14\x15\x16\x17"
+ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
+ .plen = 32,
+ .ctext = "\xd2\x96\xcd\x94\xc2\xcc\xcf\x8a"
+ "\x3a\x86\x30\x28\xb5\xe1\xdc\x0a"
+ "\x75\x86\x60\x2d\x25\x3c\xff\xf9"
+ "\x1b\x82\x66\xbe\xa6\xd6\x1a\xb1",
+ .iv_in = "\x56\x2e\x17\x99\x6d\x09\x3d\x28"
+ "\xdd\xb3\xba\x69\x5a\x2e\x6f\x58",
+ .iv_out = "\x75\x86\x60\x2d\x25\x3c\xff\xf9"
+ "\x1b\x82\x66\xbe\xa6\xd6\x1a\xb1",
+ .ivlen = 16,
+ },
+ {
+ .cipher = "aes",
+ .mode = "cbc",
+ .key = "\x8e\x73\xb0\xf7\xda\x0e\x64\x52"
+ "\xc8\x10\xf3\x2b\x80\x90\x79\xe5"
+ "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b",
+ .keylen = 24,
+ .ptext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
+ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
+ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
+ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ .plen = 64,
+ .ctext = "\x4f\x02\x1d\xb2\x43\xbc\x63\x3d"
+ "\x71\x78\x18\x3a\x9f\xa0\x71\xe8"
+ "\xb4\xd9\xad\xa9\xad\x7d\xed\xf4"
+ "\xe5\xe7\x38\x76\x3f\x69\x14\x5a"
+ "\x57\x1b\x24\x20\x12\xfb\x7a\xe0"
+ "\x7f\xa9\xba\xac\x3d\xf1\x02\xe0"
+ "\x08\xb0\xe2\x79\x88\x59\x88\x81"
+ "\xd9\x20\xa9\xe6\x4f\x56\x15\xcd",
+ .iv_in = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+ .iv_out = "\x08\xb0\xe2\x79\x88\x59\x88\x81"
+ "\xd9\x20\xa9\xe6\x4f\x56\x15\xcd",
+ .ivlen = 16,
+ },
+ {
+ .cipher = "aes",
+ .mode = "cbc",
+ .key = "\x60\x3d\xeb\x10\x15\xca\x71\xbe"
+ "\x2b\x73\xae\xf0\x85\x7d\x77\x81"
+ "\x1f\x35\x2c\x07\x3b\x61\x08\xd7"
+ "\x2d\x98\x10\xa3\x09\x14\xdf\xf4",
+ .keylen = 32,
+ .ptext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
+ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
+ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
+ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ .plen = 64,
+ .ctext = "\xf5\x8c\x4c\x04\xd6\xe5\xf1\xba"
+ "\x77\x9e\xab\xfb\x5f\x7b\xfb\xd6"
+ "\x9c\xfc\x4e\x96\x7e\xdb\x80\x8d"
+ "\x67\x9f\x77\x7b\xc6\x70\x2c\x7d"
+ "\x39\xf2\x33\x69\xa9\xd9\xba\xcf"
+ "\xa5\x30\xe2\x63\x04\x23\x14\x61"
+ "\xb2\xeb\x05\xe2\xc3\x9b\xe9\xfc"
+ "\xda\x6c\x19\x07\x8c\x6a\x9d\x1b",
+ .iv_in = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+ .iv_out = "\xb2\xeb\x05\xe2\xc3\x9b\xe9\xfc"
+ "\xda\x6c\x19\x07\x8c\x6a\x9d\x1b",
+ .ivlen = 16,
+ },
+};
diff --git a/grub-core/tests/lib/functional_test.c b/grub-core/tests/lib/functional_test.c
index 38e981f2c..4214332df 100644
--- a/grub-core/tests/lib/functional_test.c
+++ b/grub-core/tests/lib/functional_test.c
@@ -73,6 +73,7 @@ grub_functional_all_tests (grub_extcmd_context_t ctxt __attribute__ ((unused)),
grub_dl_load ("xnu_uuid_test");
grub_dl_load ("pbkdf2_test");
grub_dl_load ("signature_test");
+ grub_dl_load ("appended_signature_test");
grub_dl_load ("sleep_test");
grub_dl_load ("bswap_test");
grub_dl_load ("ctz_test");
@@ -80,6 +81,8 @@ grub_functional_all_tests (grub_extcmd_context_t ctxt __attribute__ ((unused)),
grub_dl_load ("mul_test");
grub_dl_load ("shift_test");
grub_dl_load ("asn1_test");
+ grub_dl_load ("argon2_test");
+ grub_dl_load ("crypto_cipher_mode_test");
FOR_LIST_ELEMENTS (test, grub_test_list)
ok = !grub_test_run (test) && ok;
@@ -90,17 +93,18 @@ grub_functional_all_tests (grub_extcmd_context_t ctxt __attribute__ ((unused)),
return GRUB_ERR_NONE;
}
-static grub_extcmd_t cmd;
+static grub_extcmd_t cmd, cmd_all;
GRUB_MOD_INIT (functional_test)
{
cmd = grub_register_extcmd ("functional_test", grub_functional_test, 0, 0,
"Run all loaded functional tests.", 0);
- cmd = grub_register_extcmd ("all_functional_test", grub_functional_all_tests, 0, 0,
- "Run all functional tests.", 0);
+ cmd_all = grub_register_extcmd ("all_functional_test", grub_functional_all_tests, 0, 0,
+ "Run all functional tests.", 0);
}
GRUB_MOD_FINI (functional_test)
{
grub_unregister_extcmd (cmd);
+ grub_unregister_extcmd (cmd_all);
}
diff --git a/grub-core/tests/pbkdf2_test.c b/grub-core/tests/pbkdf2_test.c
index 4eeb8b89f..ba735d4ec 100644
--- a/grub-core/tests/pbkdf2_test.c
+++ b/grub-core/tests/pbkdf2_test.c
@@ -31,6 +31,7 @@ static struct
grub_size_t Slen;
unsigned int c;
grub_size_t dkLen;
+ const gcry_md_spec_t *HMAC_variant;
const char *DK;
} vectors[] = {
/* RFC6070. */
@@ -38,6 +39,7 @@ static struct
"password", 8,
"salt", 4,
1, 20,
+ GRUB_MD_SHA1,
"\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9\xb5\x24\xaf\x60\x12"
"\x06\x2f\xe0\x37\xa6"
},
@@ -45,6 +47,7 @@ static struct
"password", 8,
"salt", 4,
2, 20,
+ GRUB_MD_SHA1,
"\xea\x6c\x01\x4d\xc7\x2d\x6f\x8c"
"\xcd\x1e\xd9\x2a\xce\x1d\x41\xf0"
"\xd8\xde\x89\x57"
@@ -53,6 +56,7 @@ static struct
"password", 8,
"salt", 4,
4096, 20,
+ GRUB_MD_SHA1,
"\x4b\x00\x79\x01\xb7\x65\x48\x9a\xbe\xad\x49\xd9\x26\xf7"
"\x21\xd0\x65\xa4\x29\xc1"
},
@@ -60,6 +64,7 @@ static struct
"passwordPASSWORDpassword", 24,
"saltSALTsaltSALTsaltSALTsaltSALTsalt", 36,
4096, 25,
+ GRUB_MD_SHA1,
"\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8\xd8\x36\x62\xc0"
"\xe4\x4a\x8b\x29\x1a\x96\x4c\xf2\xf0\x70\x38"
},
@@ -67,7 +72,89 @@ static struct
"pass\0word", 9,
"sa\0lt", 5,
4096, 16,
+ GRUB_MD_SHA1,
"\x56\xfa\x6a\xa7\x55\x48\x09\x9d\xcc\x37\xd7\xf0\x34\x25\xe0\xc3"
+ },
+ /* Re-using the above vectors for HMAC-SHA{256,512} */
+ {
+ "password", 8,
+ "salt", 4,
+ 1, 20,
+ GRUB_MD_SHA256,
+ "\x12\x0f\xb6\xcf\xfc\xf8\xb3\x2c\x43\xe7\x22\x52\x56\xc4\xf8"
+ "\x37\xa8\x65\x48\xc9"
+ },
+ {
+ "password", 8,
+ "salt", 4,
+ 2, 20,
+ GRUB_MD_SHA256,
+ "\xae\x4d\x0c\x95\xaf\x6b\x46\xd3"
+ "\x2d\x0a\xdf\xf9\x28\xf0\x6d\xd0"
+ "\x2a\x30\x3f\x8e"
+ },
+ {
+ "password", 8,
+ "salt", 4,
+ 4096, 20,
+ GRUB_MD_SHA256,
+ "\xc5\xe4\x78\xd5\x92\x88\xc8\x41\xaa\x53\x0d\xb6\x84\x5c"
+ "\x4c\x8d\x96\x28\x93\xa0"
+ },
+ {
+ "passwordPASSWORDpassword", 24,
+ "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36,
+ 4096, 25,
+ GRUB_MD_SHA256,
+ "\x34\x8c\x89\xdb\xcb\xd3\x2b\x2f\x32\xd8\x14\xb8\x11\x6e"
+ "\x84\xcf\x2b\x17\x34\x7e\xbc\x18\x00\x18\x1c"
+ },
+ {
+ "pass\0word", 9,
+ "sa\0lt", 5,
+ 4096, 16,
+ GRUB_MD_SHA256,
+ "\x89\xb6\x9d\x05\x16\xf8\x29\x89\x3c\x69\x62\x26\x65\x0a\x86\x87"
+ },
+ {
+ "password", 8,
+ "salt", 4,
+ 1, 20,
+ GRUB_MD_SHA512,
+ "\x86\x7f\x70\xcf\x1a\xde\x02\xcf\xf3\x75\x25\x99\xa3\xa5\x3d"
+ "\xc4\xaf\x34\xc7\xa6"
+ },
+ {
+ "password", 8,
+ "salt", 4,
+ 2, 20,
+ GRUB_MD_SHA512,
+ "\xe1\xd9\xc1\x6a\xa6\x81\x70\x8a"
+ "\x45\xf5\xc7\xc4\xe2\x15\xce\xb6"
+ "\x6e\x01\x1a\x2e"
+ },
+ {
+ "password", 8,
+ "salt", 4,
+ 4096, 20,
+ GRUB_MD_SHA512,
+ "\xd1\x97\xb1\xb3\x3d\xb0\x14\x3e\x01\x8b\x12\xf3\xd1\xd1"
+ "\x47\x9e\x6c\xde\xbd\xcc"
+ },
+ {
+ "passwordPASSWORDpassword", 24,
+ "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36,
+ 4096, 25,
+ GRUB_MD_SHA512,
+ "\x8c\x05\x11\xf4\xc6\xe5\x97\xc6\xac\x63\x15\xd8\xf0\x36"
+ "\x2e\x22\x5f\x3c\x50\x14\x95\xba\x23\xb8\x68"
+ },
+ {
+ "pass\0word", 9,
+ "sa\0lt", 5,
+ 4096, 16,
+ GRUB_MD_SHA512,
+ "\x9d\x9e\x9c\x4c\xd2\x1f\xe4\xbe\x24\xd5\xb8\x24\x4c\x75\x96\x65"
}
};
@@ -80,7 +167,7 @@ pbkdf2_test (void)
{
gcry_err_code_t err;
grub_uint8_t DK[32];
- err = grub_crypto_pbkdf2 (GRUB_MD_SHA1,
+ err = grub_crypto_pbkdf2 (vectors[i].HMAC_variant,
(const grub_uint8_t *) vectors[i].P,
vectors[i].Plen,
(const grub_uint8_t *) vectors[i].S,
diff --git a/grub-core/video/efi_gop.c b/grub-core/video/efi_gop.c
index 9452f5e58..1ad2e709d 100644
--- a/grub-core/video/efi_gop.c
+++ b/grub-core/video/efi_gop.c
@@ -94,7 +94,7 @@ check_protocol (void)
gop_handle = 0;
grub_dprintf ("video", "GOP: no usable mode\n");
-
+ grub_free (handles);
return 0;
}
diff --git a/grub-core/video/video.c b/grub-core/video/video.c
index 8937da745..d5bd1d079 100644
--- a/grub-core/video/video.c
+++ b/grub-core/video/video.c
@@ -89,6 +89,27 @@ grub_video_get_info_and_fini (struct grub_video_mode_info *mode_info,
return GRUB_ERR_NONE;
}
+/* Get information about connected display. */
+grub_err_t
+grub_video_get_edid (struct grub_video_edid_info *edid_info)
+{
+ grub_err_t err;
+
+ if (grub_video_adapter_active == NULL)
+ return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
+
+ if (grub_video_adapter_active->get_edid != NULL)
+ {
+ err = grub_video_adapter_active->get_edid (edid_info);
+ if (err != GRUB_ERR_NONE)
+ return err;
+ }
+ else
+ grub_memset (edid_info, 0, sizeof (*edid_info));
+
+ return GRUB_ERR_NONE;
+}
+
/* Determine optimized blitting formation for specified video mode info. */
enum grub_video_blit_format
grub_video_get_blit_format (struct grub_video_mode_info *mode_info)
diff --git a/include/grub/crypto.h b/include/grub/crypto.h
index b0d7add1d..f80ed25bb 100644
--- a/include/grub/crypto.h
+++ b/include/grub/crypto.h
@@ -34,6 +34,7 @@ typedef enum
GPG_ERR_BAD_MPI,
GPG_ERR_BAD_SECKEY,
GPG_ERR_BAD_SIGNATURE,
+ GPG_ERR_CANCELED,
GPG_ERR_CIPHER_ALGO,
GPG_ERR_CONFLICT,
GPG_ERR_DECRYPT_FAILED,
@@ -48,6 +49,7 @@ typedef enum
GPG_ERR_INV_OP,
GPG_ERR_INV_SEXP,
GPG_ERR_INV_VALUE,
+ GPG_ERR_MAC_ALGO,
GPG_ERR_MISSING_VALUE,
GPG_ERR_NO_ENCRYPTION_SCHEME,
GPG_ERR_NO_OBJ,
@@ -59,7 +61,9 @@ typedef enum
GPG_ERR_PUBKEY_ALGO,
GPG_ERR_SELFTEST_FAILED,
GPG_ERR_TOO_SHORT,
+ GPG_ERR_UNKNOWN_ALGORITHM,
GPG_ERR_UNSUPPORTED,
+ GPG_ERR_UNSUPPORTED_ALGORITHM,
GPG_ERR_WEAK_KEY,
GPG_ERR_WRONG_KEY_USAGE,
GPG_ERR_WRONG_PUBKEY_ALGO,
@@ -245,6 +249,14 @@ typedef struct gcry_md_spec
struct gcry_md_spec *next;
} gcry_md_spec_t;
+/*
+ * Clang defaults to flagging type redefinitions as warnings.
+ * A few "forward declarations" in this file are needed due
+ * to inter-relationship complexities between GRUB and libgcrypt.
+ */
+#ifdef __clang__
+#pragma GCC diagnostic ignored "-Wtypedef-redefinition"
+#endif
typedef struct gcry_md_handle*gcry_md_hd_t;
struct gcry_mpi;
@@ -499,8 +511,14 @@ void
grub_crypto_hmac_write (struct grub_crypto_hmac_handle *hnd,
const void *data,
grub_size_t datalen);
-gcry_err_code_t
+void
grub_crypto_hmac_fini (struct grub_crypto_hmac_handle *hnd, void *out);
+void
+grub_crypto_hmac_reset (struct grub_crypto_hmac_handle *hnd);
+void
+grub_crypto_hmac_final (struct grub_crypto_hmac_handle *hnd, void *out);
+void
+grub_crypto_hmac_free (struct grub_crypto_hmac_handle *hnd);
gcry_err_code_t
grub_crypto_hmac_buffer (const struct gcry_md_spec *md,
@@ -510,8 +528,10 @@ grub_crypto_hmac_buffer (const struct gcry_md_spec *md,
extern gcry_md_spec_t _gcry_digest_spec_md5;
extern gcry_md_spec_t _gcry_digest_spec_sha1;
extern gcry_md_spec_t _gcry_digest_spec_sha256;
+extern gcry_md_spec_t _gcry_digest_spec_sha384;
extern gcry_md_spec_t _gcry_digest_spec_sha512;
extern gcry_md_spec_t _gcry_digest_spec_crc32;
+extern gcry_md_spec_t _gcry_digest_spec_blake2b_512;
extern gcry_cipher_spec_t _gcry_cipher_spec_aes;
#define GRUB_MD_MD5 ((const gcry_md_spec_t *) &_gcry_digest_spec_md5)
#define GRUB_MD_SHA1 ((const gcry_md_spec_t *) &_gcry_digest_spec_sha1)
@@ -520,6 +540,41 @@ extern gcry_cipher_spec_t _gcry_cipher_spec_aes;
#define GRUB_MD_CRC32 ((const gcry_md_spec_t *) &_gcry_digest_spec_crc32)
#define GRUB_CIPHER_AES ((const gcry_cipher_spec_t *) &_gcry_cipher_spec_aes)
+/* Algorithm IDs for the KDFs. */
+enum grub_gcry_kdf_algos
+ {
+ GRUB_GCRY_KDF_NONE = 0,
+ GRUB_GCRY_KDF_ARGON2 = 64,
+ };
+
+enum grub_gcry_kdf_subalgo_argon2
+ {
+ GRUB_GCRY_KDF_ARGON2D = 0,
+ GRUB_GCRY_KDF_ARGON2I = 1,
+ GRUB_GCRY_KDF_ARGON2ID = 2
+ };
+
+typedef struct gcry_kdf_handle *gcry_kdf_hd_t;
+struct gcry_kdf_handle;
+struct gcry_kdf_thread_ops;
+
+gpg_err_code_t
+_gcry_kdf_open (gcry_kdf_hd_t *hd, int algo, int subalgo,
+ const unsigned long *param, unsigned int paramlen,
+ const void *input, grub_size_t inputlen,
+ const void *salt, grub_size_t saltlen,
+ const void *key, grub_size_t keylen,
+ const void *ad, grub_size_t adlen);
+
+gpg_err_code_t
+_gcry_kdf_compute (gcry_kdf_hd_t h, const struct gcry_kdf_thread_ops *ops);
+
+gpg_err_code_t
+_gcry_kdf_final (gcry_kdf_hd_t h, grub_size_t resultlen, void *result);
+
+void
+_gcry_kdf_close (gcry_kdf_hd_t h);
+
/* Implement PKCS#5 PBKDF2 as per RFC 2898. The PRF to use is HMAC variant
of digest supplied by MD. Inputs are the password P of length PLEN,
the salt S of length SLEN, the iteration counter C (> 0), and the
@@ -533,6 +588,15 @@ grub_crypto_pbkdf2 (const struct gcry_md_spec *md,
unsigned int c,
grub_uint8_t *DK, grub_size_t dkLen);
+gcry_err_code_t
+grub_crypto_argon2 (int subalgo,
+ const unsigned long *param, unsigned int paramlen,
+ const void *password, grub_size_t passwordlen,
+ const void *salt, grub_size_t saltlen,
+ const void *key, grub_size_t keylen,
+ const void *ad, grub_size_t adlen,
+ grub_size_t resultlen, void *result);
+
int
grub_crypto_memcmp (const void *a, const void *b, grub_size_t n);
@@ -562,11 +626,14 @@ _gcry_ct_memequal (const void *b1, const void *b2, grub_size_t len);
unsigned int
_gcry_ct_not_memequal (const void *b1, const void *b2, grub_size_t len);
-
-static inline unsigned int _gcry_get_hw_features(void)
+#if defined (GRUB_UTIL)
+static inline unsigned int _gcry_get_hw_features (void)
{
return 0;
}
+#else
+extern unsigned int _gcry_get_hw_features (void);
+#endif
void *_gcry_malloc(grub_size_t n);
void *_gcry_malloc_secure(grub_size_t n);
diff --git a/include/grub/datetime.h b/include/grub/datetime.h
index bcec636f0..17303be9c 100644
--- a/include/grub/datetime.h
+++ b/include/grub/datetime.h
@@ -54,8 +54,9 @@ void grub_unixtime2datetime (grub_int64_t nix,
static inline int
grub_datetime2unixtime (const struct grub_datetime *datetime, grub_int64_t *nix)
{
- grub_int32_t ret;
+ grub_int64_t ret;
int y4, ay;
+ bool bisextile;
const grub_uint16_t monthssum[12]
= { 0,
31,
@@ -75,15 +76,11 @@ grub_datetime2unixtime (const struct grub_datetime *datetime, grub_int64_t *nix)
const int SECPERHOUR = 60 * SECPERMIN;
const int SECPERDAY = 24 * SECPERHOUR;
const int SECPERYEAR = 365 * SECPERDAY;
- const int SECPER4YEARS = 4 * SECPERYEAR + SECPERDAY;
+ const grub_int64_t SECPER4YEARS = 4 * SECPERYEAR + SECPERDAY;
- if (datetime->year > 2038 || datetime->year < 1901)
- return 0;
if (datetime->month > 12 || datetime->month < 1)
return 0;
- /* In the period of validity of unixtime all years divisible by 4
- are bissextile*/
/* Convenience: let's have 3 consecutive non-bissextile years
at the beginning of the epoch. So count from 1973 instead of 1970 */
ret = 3 * SECPERYEAR + SECPERDAY;
@@ -91,16 +88,31 @@ grub_datetime2unixtime (const struct grub_datetime *datetime, grub_int64_t *nix)
/* Transform C divisions and modulos to mathematical ones */
y4 = ((datetime->year - 1) >> 2) - (1973 / 4);
ay = datetime->year - 1973 - 4 * y4;
- ret += y4 * SECPER4YEARS;
- ret += ay * SECPERYEAR;
+ ret += (grub_int64_t) y4 * SECPER4YEARS;
+ ret += (grub_int64_t) ay * SECPERYEAR;
- ret += monthssum[datetime->month - 1] * SECPERDAY;
- if (ay == 3 && datetime->month >= 3)
+ /*
+ * Correct above calculation (which assumes every 4 years is a leap year)
+ * to remove those "false leap years" that are divisible by 100 but not 400.
+ * Since this logic starts with seconds since 1973, 15 is used because:
+ * - (1973 - 1) / 100 = 19 (floor due to integer math)
+ * - (1973 - 1) / 400 = 4 (floor due to integer math)
+ * - 19 - 4 - 15 = 0 (we want to start with no "false leap years" at time
+ * zero of 1973)
+ */
+ ret -= ((datetime->year - 1) / 100 - (datetime->year - 1) / 400 - 15)
+ * SECPERDAY;
+
+ ret += (grub_int64_t) monthssum[datetime->month - 1] * SECPERDAY;
+ bisextile = (ay == 3
+ && (datetime->year % 100 != 0
+ || datetime->year % 400 == 0)) ? true : false;
+ if (bisextile == true && datetime->month >= 3)
ret += SECPERDAY;
ret += (datetime->day - 1) * SECPERDAY;
if ((datetime->day > months[datetime->month - 1]
- && (!ay || datetime->month != 2 || datetime->day != 29))
+ && !(bisextile == true && datetime->month == 2 && datetime->day == 29))
|| datetime->day < 1)
return 0;
diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
index 9ae908729..f7e9c46a5 100644
--- a/include/grub/efi/api.h
+++ b/include/grub/efi/api.h
@@ -389,6 +389,16 @@
{ 0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89 } \
}
+#define GRUB_EFI_MEMORY_ATTRIBUTES_TABLE_GUID \
+ { 0xdcfa911d, 0x26eb, 0x469f, \
+ { 0xa2, 0x20, 0x38, 0xb7, 0xdc, 0x46, 0x12, 0x20 } \
+ }
+
+#define GRUB_EFI_TCG2_FINAL_EVENTS_TABLE_GUID \
+ { 0x1e2ed096, 0x30e2, 0x4254, \
+ { 0xbd, 0x89, 0x86, 0x3b, 0xbe, 0xf8, 0x23, 0x25 } \
+ }
+
struct grub_efi_sal_system_table
{
grub_uint32_t signature;
diff --git a/include/grub/efi/pks.h b/include/grub/efi/pks.h
new file mode 100644
index 000000000..ff306f591
--- /dev/null
+++ b/include/grub/efi/pks.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved. This
+ * program and the accompanying materials are licensed and made available
+ * under the terms and conditions of the 2-Clause BSD License which
+ * accompanies this distribution.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * https://github.com/tianocore/edk2-staging (edk2-staging repo of tianocore),
+ * the ImageAuthentication.h file under it, and here's the copyright and license.
+ *
+ * MdePkg/Include/Guid/ImageAuthentication.h
+ *
+ * Copyright 2022, 2023, 2024, 2025 IBM Corp.
+ */
+
+#ifndef PKS_HEADER
+#define PKS_HEADER 1
+
+#include
+
+/*
+ * It is derived from EFI_CERT_X509_GUID.
+ * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h
+ */
+#define GRUB_PKS_CERT_X509_GUID \
+ (grub_guid_t) \
+ { 0xa159c0a5, 0xe494, 0xa74a, \
+ { 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72 } \
+ }
+
+/*
+ * It is derived from EFI_CERT_SHA256_GUID.
+ * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h
+ */
+#define GRUB_PKS_CERT_SHA256_GUID \
+ (grub_guid_t) \
+ { 0x2616c4c1, 0x4c50, 0x9240, \
+ { 0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28 } \
+ }
+
+/*
+ * It is derived from EFI_CERT_SHA384_GUID.
+ * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h
+ */
+#define GRUB_PKS_CERT_SHA384_GUID \
+ (grub_guid_t) \
+ { 0x07533eff, 0xd09f, 0xc948, \
+ { 0x85, 0xf1, 0x8a, 0xd5, 0x6c, 0x70, 0x1e, 0x1 } \
+ }
+
+/*
+ * It is derived from EFI_CERT_SHA512_GUID.
+ * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h
+ */
+#define GRUB_PKS_CERT_SHA512_GUID \
+ (grub_guid_t) \
+ { 0xae0f3e09, 0xc4a6, 0x504f, \
+ { 0x9f, 0x1b, 0xd4, 0x1e, 0x2b, 0x89, 0xc1, 0x9a } \
+ }
+
+/*
+ * It is derived from EFI_CERT_X509_SHA256_GUID.
+ * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h
+ */
+#define GRUB_PKS_CERT_X509_SHA256_GUID \
+ (grub_guid_t) \
+ { 0x92a4d23b, 0xc096, 0x7940, \
+ { 0xb4, 0x20, 0xfc, 0xf9, 0x8e, 0xf1, 0x03, 0xed } \
+ }
+
+/*
+ * It is derived from EFI_CERT_X509_SHA384_GUID.
+ * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h
+ */
+#define GRUB_PKS_CERT_X509_SHA384_GUID \
+ (grub_guid_t) \
+ { 0x6e877670, 0xc280, 0xe64e, \
+ { 0xaa, 0xd2, 0x28, 0xb3, 0x49, 0xa6, 0x86, 0x5b } \
+ }
+
+/*
+ * It is derived from EFI_CERT_X509_SHA512_GUID.
+ * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h
+ */
+#define GRUB_PKS_CERT_X509_SHA512_GUID \
+ (grub_guid_t) \
+ { 0x63bf6d44, 0x0225, 0xda4c, \
+ { 0xbc, 0xfa, 0x24, 0x65, 0xd2, 0xb0, 0xfe, 0x9d } \
+ }
+
+#endif
diff --git a/include/grub/emu/misc.h b/include/grub/emu/misc.h
index fefbec499..ceedc7399 100644
--- a/include/grub/emu/misc.h
+++ b/include/grub/emu/misc.h
@@ -39,7 +39,7 @@ void grub_fini_all (void);
void grub_find_zpool_from_dir (const char *dir,
char **poolname, char **poolfs);
-char *grub_make_system_path_relative_to_its_root (const char *path)
+char *EXPORT_FUNC (grub_make_system_path_relative_to_its_root) (const char *path)
WARN_UNUSED_RESULT;
int
grub_util_device_is_mapped (const char *dev);
diff --git a/include/grub/err.h b/include/grub/err.h
index 202fa8a7a..99e757a80 100644
--- a/include/grub/err.h
+++ b/include/grub/err.h
@@ -75,7 +75,8 @@ typedef enum
GRUB_ERR_BAD_SIGNATURE,
GRUB_ERR_BAD_FIRMWARE,
GRUB_ERR_STILL_REFERENCED,
- GRUB_ERR_RECURSION_DEPTH
+ GRUB_ERR_RECURSION_DEPTH,
+ GRUB_ERR_EXISTS
}
grub_err_t;
@@ -88,8 +89,11 @@ struct grub_error_saved
extern grub_err_t EXPORT_VAR(grub_errno);
extern char EXPORT_VAR(grub_errmsg)[GRUB_MAX_ERRMSG];
-grub_err_t EXPORT_FUNC(grub_error) (grub_err_t n, const char *fmt, ...)
- __attribute__ ((format (GNU_PRINTF, 2, 3)));
+grub_err_t EXPORT_FUNC(grub_error) (grub_err_t n, const char *file, const char *function, const int line, const char *fmt, ...)
+ __attribute__ ((format (GNU_PRINTF, 5, 6)));
+
+#define grub_error(n, fmt, ...) grub_error (n, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
+
void EXPORT_FUNC(grub_fatal) (const char *fmt, ...) __attribute__ ((noreturn));
void EXPORT_FUNC(grub_error_push) (void);
int EXPORT_FUNC(grub_error_pop) (void);
diff --git a/include/grub/file.h b/include/grub/file.h
index a5bf3a792..d51834e6a 100644
--- a/include/grub/file.h
+++ b/include/grub/file.h
@@ -80,6 +80,8 @@ enum grub_file_type
GRUB_FILE_TYPE_PUBLIC_KEY,
/* File holding public key to add to trused keys. */
GRUB_FILE_TYPE_PUBLIC_KEY_TRUST,
+ /* File holding x509 certificiate to add to trusted keys. */
+ GRUB_FILE_TYPE_CERTIFICATE_TRUST,
/* File of which we intend to print a blocklist to the user. */
GRUB_FILE_TYPE_PRINT_BLOCKLIST,
/* File we intend to use for test loading or testing speed. */
@@ -113,6 +115,8 @@ enum grub_file_type
GRUB_FILE_TYPE_HASHLIST,
/* File hashed by hashsum. */
GRUB_FILE_TYPE_TO_HASH,
+ /* File holding certificiate/binary hash to add to db/dbx. */
+ GRUB_FILE_TYPE_HASH_TRUST,
/* Keyboard layout. */
GRUB_FILE_TYPE_KEYBOARD_LAYOUT,
/* Picture file. */
@@ -186,9 +190,10 @@ typedef enum grub_file_filter_id
GRUB_FILE_FILTER_GZIO,
GRUB_FILE_FILTER_XZIO,
GRUB_FILE_FILTER_LZOPIO,
+ GRUB_FILE_FILTER_ZSTDIO,
GRUB_FILE_FILTER_MAX,
GRUB_FILE_FILTER_COMPRESSION_FIRST = GRUB_FILE_FILTER_GZIO,
- GRUB_FILE_FILTER_COMPRESSION_LAST = GRUB_FILE_FILTER_LZOPIO,
+ GRUB_FILE_FILTER_COMPRESSION_LAST = GRUB_FILE_FILTER_ZSTDIO,
} grub_file_filter_id_t;
typedef grub_file_t (*grub_file_filter_t) (grub_file_t in, enum grub_file_type type);
diff --git a/include/grub/fs.h b/include/grub/fs.h
index df4c93b16..89e4d2b9b 100644
--- a/include/grub/fs.h
+++ b/include/grub/fs.h
@@ -132,4 +132,6 @@ grub_fs_unregister (grub_fs_t fs)
grub_fs_t EXPORT_FUNC(grub_fs_probe) (grub_device_t device);
+#define GRUB_ENV_BTRFS_OFFSET (256 * 1024)
+
#endif /* ! GRUB_FS_HEADER */
diff --git a/include/grub/hwfeatures-gcry.h b/include/grub/hwfeatures-gcry.h
new file mode 100644
index 000000000..2884f054a
--- /dev/null
+++ b/include/grub/hwfeatures-gcry.h
@@ -0,0 +1,26 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2025 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 HWF_GCRY_HEADER
+#define HWF_GCRY_HEADER 1
+
+extern bool grub_gcry_hwf_enabled (void);
+extern void grub_enable_gcry_hwf (void);
+extern void grub_reset_gcry_hwf (void);
+
+#endif /* HWF_GCRY_HEADER */
diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h
index c445d0499..157ed57be 100644
--- a/include/grub/ieee1275/ieee1275.h
+++ b/include/grub/ieee1275/ieee1275.h
@@ -24,6 +24,9 @@
#include
#include
+#define IEEE1275_CELL_INVALID ((grub_ieee1275_cell_t) -1)
+#define IEEE1275_CELL_NOT_FOUND ((grub_int32_t) -7)
+
#define GRUB_IEEE1275_CELL_FALSE ((grub_ieee1275_cell_t) 0)
#define GRUB_IEEE1275_CELL_TRUE ((grub_ieee1275_cell_t) -1)
diff --git a/include/grub/ieee1275/tpm.h b/include/grub/ieee1275/tpm.h
index fe5cb4713..c34e8edc0 100644
--- a/include/grub/ieee1275/tpm.h
+++ b/include/grub/ieee1275/tpm.h
@@ -27,4 +27,9 @@ extern grub_ieee1275_ihandle_t grub_ieee1275_tpm_ihandle;
extern grub_err_t grub_ieee1275_tpm_init (void);
+extern grub_err_t grub_ieee1275_ibmvtpm_2hash_ext_log (grub_uint8_t pcrindex,
+ grub_uint32_t eventtype,
+ const char *description,
+ grub_size_t description_size,
+ void *buf, grub_size_t size);
#endif
diff --git a/include/grub/kernel.h b/include/grub/kernel.h
index 6121c1e66..9f3e2031f 100644
--- a/include/grub/kernel.h
+++ b/include/grub/kernel.h
@@ -28,7 +28,8 @@ enum
OBJ_TYPE_MEMDISK,
OBJ_TYPE_CONFIG,
OBJ_TYPE_PREFIX,
- OBJ_TYPE_PUBKEY,
+ OBJ_TYPE_GPG_PUBKEY,
+ OBJ_TYPE_X509_PUBKEY,
OBJ_TYPE_DTB,
OBJ_TYPE_DISABLE_SHIM_LOCK,
OBJ_TYPE_DISABLE_CLI
diff --git a/include/grub/lockdown.h b/include/grub/lockdown.h
index 40531fa82..ebfee4bf0 100644
--- a/include/grub/lockdown.h
+++ b/include/grub/lockdown.h
@@ -24,7 +24,8 @@
#define GRUB_LOCKDOWN_DISABLED 0
#define GRUB_LOCKDOWN_ENABLED 1
-#ifdef GRUB_MACHINE_EFI
+#if defined(GRUB_MACHINE_EFI) || \
+ (defined(__powerpc__) && defined(GRUB_MACHINE_IEEE1275))
extern void
EXPORT_FUNC (grub_lockdown) (void);
extern int
diff --git a/include/grub/menu.h b/include/grub/menu.h
index ee2b5e910..8d06e8400 100644
--- a/include/grub/menu.h
+++ b/include/grub/menu.h
@@ -20,6 +20,20 @@
#ifndef GRUB_MENU_HEADER
#define GRUB_MENU_HEADER 1
+struct grub_blsuki_entry
+{
+ struct grub_blsuki_entry *next;
+ struct grub_blsuki_entry **prev;
+ struct keyval **keyvals;
+ grub_size_t keyvals_size;
+ int nkeyvals;
+ char *filename;
+ char *dirname;
+ char *devid;
+ bool visible;
+};
+typedef struct grub_blsuki_entry grub_blsuki_entry_t;
+
struct grub_menu_entry_class
{
char *name;
@@ -60,6 +74,9 @@ struct grub_menu_entry
/* The next element. */
struct grub_menu_entry *next;
+
+ /* BLS used to populate the entry */
+ grub_blsuki_entry_t *blsuki;
};
typedef struct grub_menu_entry *grub_menu_entry_t;
diff --git a/include/grub/misc.h b/include/grub/misc.h
index e087e7b3e..1bb63231a 100644
--- a/include/grub/misc.h
+++ b/include/grub/misc.h
@@ -35,9 +35,10 @@
#define ARRAY_SIZE(array) (sizeof (array) / sizeof (array[0]))
#define COMPILE_TIME_ASSERT(cond) switch (0) { case 1: case !(cond): ; }
-#define grub_dprintf(condition, ...) grub_real_dprintf(GRUB_FILE, __LINE__, condition, __VA_ARGS__)
+#define grub_dprintf(condition, ...) grub_real_dprintf(GRUB_FILE, __FUNCTION__, __LINE__, condition, __VA_ARGS__)
void *EXPORT_FUNC(grub_memmove) (void *dest, const void *src, grub_size_t n);
+void *EXPORT_FUNC(grub_memcpy) (void *dest, const void *src, grub_size_t n);
char *EXPORT_FUNC(grub_strcpy) (char *dest, const char *src);
static inline char *
@@ -103,13 +104,6 @@ grub_strlcpy (char *dest, const char *src, grub_size_t size)
return res;
}
-/* XXX: If grub_memmove is too slow, we must implement grub_memcpy. */
-static inline void *
-grub_memcpy (void *dest, const void *src, grub_size_t n)
-{
- return grub_memmove (dest, src, n);
-}
-
#if defined(__x86_64__) && !defined (GRUB_UTIL)
#if defined (__MINGW32__) || defined (__CYGWIN__) || defined (__MINGW64__)
#define GRUB_ASM_ATTR __attribute__ ((sysv_abi))
@@ -126,6 +120,9 @@ char *EXPORT_FUNC(grub_strchr) (const char *s, int c);
char *EXPORT_FUNC(grub_strrchr) (const char *s, int c);
int EXPORT_FUNC(grub_strword) (const char *s, const char *w);
+char *EXPORT_FUNC(grub_strtok_r) (char *s, const char *delim, char **save_ptr);
+char *EXPORT_FUNC(grub_strtok) (char *s, const char *delim);
+
/* Copied from gnulib.
Written by Bruno Haible , 2005. */
static inline char *
@@ -410,9 +407,10 @@ grub_puts (const char *s)
int EXPORT_FUNC(grub_puts_) (const char *s);
int EXPORT_FUNC(grub_debug_enabled) (const char *condition);
void EXPORT_FUNC(grub_real_dprintf) (const char *file,
+ const char *function,
const int line,
const char *condition,
- const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 4, 5)));
+ const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 5, 6)));
int EXPORT_FUNC(grub_printf) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2)));
int EXPORT_FUNC(grub_printf_) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2)));
int EXPORT_FUNC(grub_vprintf) (const char *fmt, va_list args);
diff --git a/include/grub/mm.h b/include/grub/mm.h
index 51ec0b8f9..06956484c 100644
--- a/include/grub/mm.h
+++ b/include/grub/mm.h
@@ -100,6 +100,7 @@ extern int EXPORT_VAR(grub_mm_debug);
void EXPORT_FUNC(grub_mm_dump_free) (void);
void EXPORT_FUNC(grub_mm_dump) (unsigned lineno);
+void EXPORT_FUNC(grub_mm_dump_regions) (void);
#define grub_calloc(nmemb, size) \
grub_debug_calloc (GRUB_FILE, __LINE__, nmemb, size)
diff --git a/include/grub/normal.h b/include/grub/normal.h
index 218cbabcc..d0150e3c2 100644
--- a/include/grub/normal.h
+++ b/include/grub/normal.h
@@ -145,7 +145,7 @@ grub_normal_add_menu_entry (int argc, const char **args, char **classes,
const char *id,
const char *users, const char *hotkey,
const char *prefix, const char *sourcecode,
- int submenu);
+ int submenu, grub_blsuki_entry_t *blsuki);
grub_err_t
grub_normal_set_password (const char *user, const char *password);
diff --git a/include/grub/powerpc/ieee1275/ieee1275.h b/include/grub/powerpc/ieee1275/ieee1275.h
index 4eb207018..4b9966dd4 100644
--- a/include/grub/powerpc/ieee1275/ieee1275.h
+++ b/include/grub/powerpc/ieee1275/ieee1275.h
@@ -28,4 +28,40 @@ typedef grub_uint32_t grub_ieee1275_cell_t;
#define PRIxGRUB_IEEE1275_CELL_T PRIxGRUB_UINT32_T
#define PRIuGRUB_IEEE1275_CELL_T PRIuGRUB_UINT32_T
+#ifdef __powerpc__
+/* The maximum object size interface name for a PKS object. */
+#define GRUB_PKS_MAX_OBJ_INTERFACE "pks-max-object-size"
+
+/* PKS read object and read sbvar interface name. */
+#define GRUB_PKS_READ_OBJ_INTERFACE "pks-read-object"
+#define GRUB_PKS_READ_SBVAR_INTERFACE "pks-read-sbvar"
+
+/* PKS read object label for secure boot version. */
+#define GRUB_SB_VERSION_KEY_NAME "SB_VERSION"
+#define GRUB_SB_VERSION_KEY_LEN (sizeof (GRUB_SB_VERSION_KEY_NAME) - 1)
+
+/* PKS consumer type for firmware. */
+#define GRUB_PKS_CONSUMER_FW ((grub_uint32_t) 1)
+
+/* PKS read secure boot variable request type for db and dbx. */
+#define GRUB_PKS_SBVAR_DB ((grub_uint32_t) 1)
+#define GRUB_PKS_SBVAR_DBX ((grub_uint32_t) 2)
+
+extern grub_int32_t
+grub_ieee1275_test (const char *interface_name);
+
+extern grub_int32_t
+grub_ieee1275_pks_max_object_size (grub_uint32_t *result);
+
+extern grub_int32_t
+grub_ieee1275_pks_read_object (const grub_uint32_t consumer, const char *label,
+ const grub_uint32_t label_len, const grub_uint32_t buffer_len,
+ grub_uint8_t *buffer, grub_uint32_t *data_len,
+ grub_uint32_t *policies);
+
+extern grub_int32_t
+grub_ieee1275_pks_read_sbvar (const grub_uint32_t sbvar_flags, const grub_uint32_t sbvar_type,
+ const grub_uint32_t buffer_len, grub_uint8_t *buffer,
+ grub_size_t *data_len);
+#endif /* __powerpc__ */
#endif /* ! GRUB_IEEE1275_MACHINE_HEADER */
diff --git a/include/grub/powerpc/ieee1275/platform_keystore.h b/include/grub/powerpc/ieee1275/platform_keystore.h
new file mode 100644
index 000000000..931ada224
--- /dev/null
+++ b/include/grub/powerpc/ieee1275/platform_keystore.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved. This
+ * program and the accompanying materials are licensed and made available
+ * under the terms and conditions of the 2-Clause BSD License which
+ * accompanies this distribution.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * https://github.com/tianocore/edk2-staging (edk2-staging repo of tianocore),
+ * the ImageAuthentication.h file under it, and here's the copyright and license.
+ *
+ * MdePkg/Include/Guid/ImageAuthentication.h
+ *
+ * Copyright 2022, 2023, 2024, 2025 IBM Corp.
+ */
+
+#ifndef PLATFORM_KEYSTORE_HEADER
+#define PLATFORM_KEYSTORE_HEADER 1
+
+#include
+#include
+#include
+
+/*
+ * It is derived from EFI_SIGNATURE_DATA
+ * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h
+ *
+ * The structure of an EFI Signature Database (ESD). */
+struct grub_esd
+{
+ /*
+ * An identifier which identifies the agent which added the signature to
+ * the list.
+ */
+ grub_packed_guid_t signature_owner;
+ /* The format of the signature is defined by the SignatureType. */
+ grub_uint8_t signature_data[];
+} GRUB_PACKED;
+typedef struct grub_esd grub_esd_t;
+
+/*
+ * It is derived from EFI_SIGNATURE_LIST
+ * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h
+ *
+ * The structure of an EFI Signature List (ESL). */
+struct grub_esl
+{
+ /* Type of the signature. GUID signature types are defined in below. */
+ grub_packed_guid_t signature_type;
+ /* Total size of the signature list, including this header. */
+ grub_uint32_t signature_list_size;
+ /* Size of the signature header which precedes the array of signatures. */
+ grub_uint32_t signature_header_size;
+ /* Size of each signature.*/
+ grub_uint32_t signature_size;
+} GRUB_PACKED;
+typedef struct grub_esl grub_esl_t;
+
+/* The structure of a PKS Signature Database (SD). */
+struct grub_pks_sd
+{
+ grub_packed_guid_t guid; /* Signature type. */
+ grub_uint8_t *data; /* Signature data. */
+ grub_size_t data_size; /* Size of signature data. */
+} GRUB_PACKED;
+typedef struct grub_pks_sd grub_pks_sd_t;
+
+/* The structure of a Platform KeyStore (PKS). */
+struct grub_pks
+{
+ grub_pks_sd_t *db; /* Signature database. */
+ grub_pks_sd_t *dbx; /* Forbidden signature database. */
+ grub_uint32_t db_entries; /* Size of signature database. */
+ grub_uint32_t dbx_entries;/* Size of forbidden signature database. */
+ bool db_exists; /* Flag to indicate if the db exists or not in PKS. */
+};
+typedef struct grub_pks grub_pks_t;
+
+#if defined(__powerpc__)
+/* Initialization of the Platform Keystore. */
+extern void
+grub_pks_keystore_init (void);
+
+/* Platform KeyStore db and dbx. */
+extern grub_pks_t *
+EXPORT_FUNC (grub_pks_get_keystore) (void);
+
+/* Free allocated memory. */
+extern void
+EXPORT_FUNC (grub_pks_free_data) (void);
+#else
+static inline grub_pks_t *
+grub_pks_get_keystore (void)
+{
+ return NULL;
+}
+
+static inline void
+grub_pks_free_data (void)
+{
+}
+#endif /* __powerpc__ */
+#endif
diff --git a/include/grub/relocator_private.h b/include/grub/relocator_private.h
index d8e972e01..273add76d 100644
--- a/include/grub/relocator_private.h
+++ b/include/grub/relocator_private.h
@@ -27,6 +27,7 @@ extern grub_size_t grub_relocator_align;
extern grub_size_t grub_relocator_forward_size;
extern grub_size_t grub_relocator_backward_size;
extern grub_size_t grub_relocator_jumper_size;
+extern grub_size_t grub_relocator_preamble_size;
void
grub_cpu_relocator_init (void);
@@ -39,6 +40,7 @@ void grub_cpu_relocator_forward (void *rels, void *src, void *tgt,
void grub_cpu_relocator_backward (void *rels, void *src, void *tgt,
grub_size_t size);
void grub_cpu_relocator_jumper (void *rels, grub_addr_t addr);
+void grub_cpu_relocator_preamble (void *rels);
/* Remark: GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT_LOG = 1 or 2
aren't supported. */
diff --git a/include/grub/tpm.h b/include/grub/tpm.h
index d09783dac..fd0956b39 100644
--- a/include/grub/tpm.h
+++ b/include/grub/tpm.h
@@ -39,6 +39,7 @@
grub_err_t grub_tpm_measure (unsigned char *buf, grub_size_t size,
grub_uint8_t pcr, const char *description);
int grub_tpm_present (void);
+grub_uint32_t grub_tpm2_active_pcr_banks (void);
static inline bool
grub_is_tpm_fail_fatal (void)
diff --git a/include/grub/types.h b/include/grub/types.h
index 45079bf65..b3ba762fc 100644
--- a/include/grub/types.h
+++ b/include/grub/types.h
@@ -379,6 +379,8 @@ struct grub_guid
} __attribute__ ((aligned(4)));
typedef struct grub_guid grub_guid_t;
+#define GRUB_GUID_SIZE (sizeof (grub_guid_t))
+
struct grub_packed_guid
{
grub_uint32_t data1;
@@ -388,4 +390,6 @@ struct grub_packed_guid
} GRUB_PACKED;
typedef struct grub_packed_guid grub_packed_guid_t;
+#define GRUB_PACKED_GUID_SIZE (sizeof (grub_packed_guid_t))
+
#endif /* ! GRUB_TYPES_HEADER */
diff --git a/include/grub/util/install.h b/include/grub/util/install.h
index 5c0a52ca2..6f27e2e42 100644
--- a/include/grub/util/install.h
+++ b/include/grub/util/install.h
@@ -69,6 +69,10 @@
N_("disable shim_lock verifier"), 0 }, \
{ "disable-cli", GRUB_INSTALL_OPTIONS_DISABLE_CLI, 0, 0, \
N_("disabled command line interface access"), 0 }, \
+ { "x509key", 'x', N_("FILE"), 0, \
+ N_("embed FILE as an x509 certificate for appended signature checking"), 0}, \
+ { "appended-signature-size", GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE, \
+ "SIZE", 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), 1}, \
{ "verbose", 'v', 0, 0, \
N_("print verbose messages."), 1 }
@@ -132,7 +136,8 @@ enum grub_install_options {
GRUB_INSTALL_OPTIONS_DTB,
GRUB_INSTALL_OPTIONS_SBAT,
GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK,
- GRUB_INSTALL_OPTIONS_DISABLE_CLI
+ GRUB_INSTALL_OPTIONS_DISABLE_CLI,
+ GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE
};
extern char *grub_install_source_directory;
@@ -190,9 +195,10 @@ grub_install_generate_image (const char *dir, const char *prefix,
const char *outname, char *mods[],
char *memdisk_path, char **pubkey_paths,
size_t npubkeys,
+ char **x509key_paths, size_t nx509keys,
char *config_path,
const struct grub_install_image_target_desc *image_target,
- int note,
+ int note, size_t appsig_size,
grub_compression_t comp, const char *dtb_file,
const char *sbat_path, const int disable_shim_lock,
const int disable_cli);
diff --git a/include/grub/util/misc.h b/include/grub/util/misc.h
index e9e0a6724..bfce06558 100644
--- a/include/grub/util/misc.h
+++ b/include/grub/util/misc.h
@@ -36,7 +36,7 @@ char *grub_util_read_image (const char *path);
void grub_util_load_image (const char *path, char *buf);
void grub_util_write_image (const char *img, size_t size, FILE *out,
const char *name);
-void grub_util_write_image_at (const void *img, size_t size, off_t offset,
+void grub_util_write_image_at (const void *img, size_t size, grub_off_t offset,
FILE *out, const char *name);
char *make_system_path_relative_to_its_root (const char *path);
diff --git a/include/grub/util/mkimage.h b/include/grub/util/mkimage.h
index 9d74f82c5..0d40383eb 100644
--- a/include/grub/util/mkimage.h
+++ b/include/grub/util/mkimage.h
@@ -51,12 +51,12 @@ grub_mkimage_load_image64 (const char *kernel_path,
const struct grub_install_image_target_desc *image_target);
void
grub_mkimage_generate_elf32 (const struct grub_install_image_target_desc *image_target,
- int note, char *sbat, char **core_img, size_t *core_size,
+ int note, char *sbat, size_t appsig_size, char **core_img, size_t *core_size,
Elf32_Addr target_addr,
struct grub_mkimage_layout *layout);
void
grub_mkimage_generate_elf64 (const struct grub_install_image_target_desc *image_target,
- int note, char *sbat, char **core_img, size_t *core_size,
+ int note, char *sbat, size_t appsig_size, char **core_img, size_t *core_size,
Elf64_Addr target_addr,
struct grub_mkimage_layout *layout);
diff --git a/include/grub/video.h b/include/grub/video.h
index 9dac0f379..761ee994a 100644
--- a/include/grub/video.h
+++ b/include/grub/video.h
@@ -445,6 +445,8 @@ grub_err_t EXPORT_FUNC (grub_video_get_info) (struct grub_video_mode_info *mode_
grub_err_t EXPORT_FUNC (grub_video_get_info_and_fini) (struct grub_video_mode_info *mode_info,
void **framebuffer);
+grub_err_t EXPORT_FUNC (grub_video_get_edid) (struct grub_video_edid_info *edid_info);
+
enum grub_video_blit_format EXPORT_FUNC(grub_video_get_blit_format) (struct grub_video_mode_info *mode_info);
grub_err_t grub_video_set_palette (unsigned int start, unsigned int count,
diff --git a/include/grub/x86_64/cpuid.h b/include/grub/x86_64/cpuid.h
new file mode 100644
index 000000000..acd93e3a7
--- /dev/null
+++ b/include/grub/x86_64/cpuid.h
@@ -0,0 +1 @@
+#include
diff --git a/include/grub/x86_64/efi/hwfeatures-gcry.h b/include/grub/x86_64/efi/hwfeatures-gcry.h
new file mode 100644
index 000000000..a8e671058
--- /dev/null
+++ b/include/grub/x86_64/efi/hwfeatures-gcry.h
@@ -0,0 +1,25 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2025 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 HWF_GCRY_X86_64_EFI_HEADER
+#define HWF_GCRY_X86_64_EFI_HEADER 1
+
+extern void grub_enable_gcry_hwf_x86_64_efi (void);
+extern void grub_reset_gcry_hwf_x86_64_efi (void);
+
+#endif /* HWF_GCRY_X86_64_EFI_HEADER */
diff --git a/include/grub/xen.h b/include/grub/xen.h
index 91cb7cf81..7f9efee80 100644
--- a/include/grub/xen.h
+++ b/include/grub/xen.h
@@ -89,6 +89,8 @@ void grub_console_init (void);
void grub_xendisk_fini (void);
void grub_xendisk_init (void);
+void grub_parse_xen_cmdline (void);
+
#ifdef __x86_64__
typedef grub_uint64_t grub_xen_mfn_t;
#else
diff --git a/include/xen/xen.h b/include/xen/xen.h
index 692f97a5b..4c21fb5ef 100644
--- a/include/xen/xen.h
+++ b/include/xen/xen.h
@@ -823,8 +823,13 @@ struct start_info {
/* (PFN of pre-loaded module if */
/* SIF_MOD_START_PFN set in flags). */
unsigned long mod_len; /* Size (bytes) of pre-loaded module. */
-#define MAX_GUEST_CMDLINE 1024
- int8_t cmd_line[MAX_GUEST_CMDLINE];
+ /*
+ * cmd_line will contain a NUL-termianted string if it contains valid
+ * data, but it MAY be invalid and not contain a NUL byte at all. Code
+ * that accesses cmd_line MUST NOT assume it is NUL-terminated.
+ */
+#define GRUB_XEN_MAX_GUEST_CMDLINE 1024
+ int8_t cmd_line[GRUB_XEN_MAX_GUEST_CMDLINE];
/* The pfn range here covers both page table and p->m table frames. */
unsigned long first_p2m_pfn;/* 1st pfn forming initial P->M table. */
unsigned long nr_p2m_frames;/* # of pfns forming initial P->M table. */
diff --git a/linguas.sh b/linguas.sh
index b95ad4f7d..45cdf2ac1 100755
--- a/linguas.sh
+++ b/linguas.sh
@@ -1,5 +1,10 @@
#!/bin/sh
+SDIR=$(realpath -e "$0")
+SDIR=${SDIR%/*}
+
+cd "$SDIR"
+
rsync -Lrtvz translationproject.org::tp/latest/grub/ po
autogenerated="en@quot en@hebrew de@hebrew en@cyrillic en@greek en@arabic en@piglatin de_CH"
diff --git a/po/arabic.sed b/po/arabic.sed
index 3fbee7248..50bede0f8 100644
--- a/po/arabic.sed
+++ b/po/arabic.sed
@@ -81,3 +81,5 @@ s,%\([0-9]*\)زو,%\1zu,g
s,%\([0-9]*\)كس,%\1x,g
s,%\([0-9]*\)لكس,%\1lx,g
s,%\([0-9]*\)للكس,%\1llx,g
+
+s,\\ن,\\n,g
diff --git a/po/cyrillic.sed b/po/cyrillic.sed
index 472f09529..d3db38838 100644
--- a/po/cyrillic.sed
+++ b/po/cyrillic.sed
@@ -104,3 +104,5 @@ s,%\([0-9]*\)зу,%\1zu,g
s,%\([0-9]*\)ѯ,%\1x,g
s,%\([0-9]*\)лѯ,%\1lx,g
s,%\([0-9]*\)ллѯ,%\1llx,g
+
+s,\\н,\\n,g
diff --git a/po/greek.sed b/po/greek.sed
index 0e81625fb..1ace5fcd2 100644
--- a/po/greek.sed
+++ b/po/greek.sed
@@ -106,3 +106,5 @@ s,%\([0-9]*\)ζυ,%\1zu,g
s,%\([0-9]*\)ξ,%\1x,g
s,%\([0-9]*\)λξ,%\1lx,g
s,%\([0-9]*\)λλξ,%\1llx,g
+
+s,\\ν,\\n,g
diff --git a/po/hebrew.sed b/po/hebrew.sed
index 33174bbdc..ce59e576e 100644
--- a/po/hebrew.sed
+++ b/po/hebrew.sed
@@ -89,3 +89,5 @@ s,%\([0-9]*\)זוּ,%\1zu,g
s,%\([0-9]*\)כּס,%\1x,g
s,%\([0-9]*\)לכּס,%\1lx,g
s,%\([0-9]*\)ללכּס,%\1llx,g
+
+s,\\נ,\\n,g
diff --git a/tests/ahci_test.in b/tests/ahci_test.in
index 70646a24e..ffb7cf48d 100644
--- a/tests/ahci_test.in
+++ b/tests/ahci_test.in
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with GRUB. If not, see .
-set -e
+set -ex
grubshell=@builddir@/grub-shell
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/asn1_test.in b/tests/asn1_test.in
index 8f18ee6bb..a9b82dd0e 100644
--- a/tests/asn1_test.in
+++ b/tests/asn1_test.in
@@ -1,5 +1,5 @@
#! @BUILD_SHEBANG@
-set -e
+set -ex
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/btrfs_test.in b/tests/btrfs_test.in
index 0d098c9a2..900f56379 100644
--- a/tests/btrfs_test.in
+++ b/tests/btrfs_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if [ "x$EUID" = "x" ] ; then
EUID=`id -u`
diff --git a/tests/cdboot_test.in b/tests/cdboot_test.in
index f00cdec58..ec8943b16 100644
--- a/tests/cdboot_test.in
+++ b/tests/cdboot_test.in
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with GRUB. If not, see .
-set -e
+set -ex
grubshell=@builddir@/grub-shell
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/core_compress_test.in b/tests/core_compress_test.in
index 24a811418..0b97c9557 100644
--- a/tests/core_compress_test.in
+++ b/tests/core_compress_test.in
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with GRUB. If not, see .
-set -e
+set -ex
grubshell=@builddir@/grub-shell
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/cpio_test.in b/tests/cpio_test.in
index e2e668cf6..74759419e 100644
--- a/tests/cpio_test.in
+++ b/tests/cpio_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if ! which cpio >/dev/null 2>&1; then
echo "cpio not installed; cannot test cpio."
diff --git a/tests/date_unit_test.c b/tests/date_unit_test.c
index 99774f199..08edc54df 100644
--- a/tests/date_unit_test.c
+++ b/tests/date_unit_test.c
@@ -25,12 +25,13 @@
#include
static void
-date_test (grub_int32_t v)
+date_test (grub_int64_t v)
{
struct grub_datetime dt;
time_t t = v;
struct tm *g;
int w;
+ grub_int64_t back = 0;
g = gmtime (&t);
@@ -38,28 +39,56 @@ date_test (grub_int32_t v)
w = grub_get_weekday (&dt);
- grub_test_assert (g->tm_sec == dt.second, "time %d bad second: %d vs %d", v,
+ grub_datetime2unixtime (&dt, &back);
+
+ grub_test_assert (g->tm_sec == dt.second, "time %lld bad second: %d vs %d", (long long) v,
g->tm_sec, dt.second);
- grub_test_assert (g->tm_min == dt.minute, "time %d bad minute: %d vs %d", v,
+ grub_test_assert (g->tm_min == dt.minute, "time %lld bad minute: %d vs %d", (long long) v,
g->tm_min, dt.minute);
- grub_test_assert (g->tm_hour == dt.hour, "time %d bad hour: %d vs %d", v,
+ grub_test_assert (g->tm_hour == dt.hour, "time %lld bad hour: %d vs %d", (long long) v,
g->tm_hour, dt.hour);
- grub_test_assert (g->tm_mday == dt.day, "time %d bad day: %d vs %d", v,
+ grub_test_assert (g->tm_mday == dt.day, "time %lld bad day: %d vs %d", (long long) v,
g->tm_mday, dt.day);
- grub_test_assert (g->tm_mon + 1 == dt.month, "time %d bad month: %d vs %d", v,
+ grub_test_assert (g->tm_mon + 1 == dt.month, "time %lld bad month: %d vs %d",(long long) v,
g->tm_mon + 1, dt.month);
grub_test_assert (g->tm_year + 1900 == dt.year,
- "time %d bad year: %d vs %d", v,
+ "time %lld bad year: %d vs %d", (long long) v,
g->tm_year + 1900, dt.year);
- grub_test_assert (g->tm_wday == w, "time %d bad week day: %d vs %d", v,
+ grub_test_assert (g->tm_wday == w, "time %lld bad week day: %d vs %d", (long long) v,
g->tm_wday, w);
+ grub_test_assert (back == v, "time %lld bad back transform: %lld", (long long) v,
+ (long long) back);
}
static void
date_test_iter (void)
{
- grub_int32_t tests[] = { -1, 0, +1, -2133156255, GRUB_INT32_MIN,
+ /*
+ * Test several interesting UNIX timestamps in 32-bit time:
+ * 1. -1: 1969-12-31 23:59:59 - Just before EPOCH
+ * 2. 0: 1970-01-01 00:00:00 - EPOCH
+ * 3. +1: 1970-01-01 00:00:01 - Just after EPOCH
+ * 4. 978224552: 2000-12-31 01:02:32 - Leap year, after Feb
+ * 5. -2133156255: 1902-05-28 16:35:45 - Nominal value
+ * 6. -2110094321: 1903-02-19 14:41:19 - Nominal value
+ * 7. GRUB_INT32_MIN: 1901-12-13 20:45:52 - 32-bit Min value
+ * 8. GRUB_INT32_MAX: 2038-01-19 03:14:07 - 32-bit Max value
+ */
+ grub_int32_t tests[] = { -1, 0, +1, 978224552, -2133156255, -2110094321, GRUB_INT32_MIN,
GRUB_INT32_MAX };
+ /*
+ * Test several known UNIX timestamps outside 32-bit time:
+ * 1. 5774965200: 2152-12-31 21:00:00 - Leap year
+ * 2. 4108700725: 2100-03-14 09:45:25 - Not a leap year
+ * 3. -5029179792: 1810-08-19 21:36:48 - Not a leap year
+ * 4. -62135596799: 0001-01-01 00:00:00 - Minimum AD
+ * 5. 253402300799: 9999-12-31 23:59:59 - Maximum 4 digit year
+ */
+ grub_int64_t tests64[] = { (grub_int64_t) 5774965200,
+ (grub_int64_t) 4108700725,
+ (grub_int64_t) -5029179792,
+ (grub_int64_t) -62135596799,
+ (grub_int64_t) 253402300799 };
unsigned i;
for (i = 0; i < ARRAY_SIZE (tests); i++)
@@ -71,6 +100,34 @@ date_test_iter (void)
date_test (x);
date_test (-x);
}
+
+ if (sizeof (time_t) > 4)
+ {
+ for (i = 0; i < ARRAY_SIZE (tests64); i++)
+ date_test (tests64[i]);
+ for (i = 0; i < 5000000; i++)
+ {
+ /*
+ * Test some pseudo-random dates/times between 1970 and 9999
+ * "117" is used to scale the random 32-bit int from range
+ * 0..2147483648 to 0..251255586816. This is reasonably
+ * close to max 9999 date represented by 253402300799.
+ */
+ grub_int64_t x = (grub_int64_t) rand () * (grub_int64_t) 117;
+ date_test (x);
+ }
+ for (i = 0; i < 5000000; i++)
+ {
+ /*
+ * Test some pseudo-random dates/times between 0001 and 1969
+ * "-28" is used to scale the random 32-bit int from range
+ * 0..2147483648 to -60129542144..0. This is reasonably
+ * close to min 0001 date represented by -62135596799.
+ */
+ grub_int64_t x = (grub_int64_t) rand () * (grub_int64_t) -28;
+ date_test (x);
+ }
+ }
}
GRUB_UNIT_TEST ("date_unit_test", date_test_iter);
diff --git a/tests/ehci_test.in b/tests/ehci_test.in
index bf823a5de..7aff6df94 100644
--- a/tests/ehci_test.in
+++ b/tests/ehci_test.in
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with GRUB. If not, see .
-set -e
+set -ex
grubshell=@builddir@/grub-shell
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/erofs_test.in b/tests/erofs_test.in
index 51111627a..e0dec8d3a 100644
--- a/tests/erofs_test.in
+++ b/tests/erofs_test.in
@@ -1,14 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
-
-if [ "x$EUID" = "x" ] ; then
- EUID=`id -u`
-fi
-
-if [ "$EUID" != 0 ] ; then
- exit 99
-fi
+set -ex
if ! which mkfs.erofs >/dev/null 2>&1; then
echo "mkfs.erofs not installed; cannot test erofs."
diff --git a/tests/example_scripted_test.in b/tests/example_scripted_test.in
index 783b7f138..0f32c79fa 100644
--- a/tests/example_scripted_test.in
+++ b/tests/example_scripted_test.in
@@ -1,4 +1,4 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
true
diff --git a/tests/exfat_test.in b/tests/exfat_test.in
index 7939f25d2..63300b8a5 100644
--- a/tests/exfat_test.in
+++ b/tests/exfat_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if [ "x$EUID" = "x" ] ; then
EUID=`id -u`
diff --git a/tests/ext234_test.in b/tests/ext234_test.in
index 4df696710..3761238db 100644
--- a/tests/ext234_test.in
+++ b/tests/ext234_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if [ "x$EUID" = "x" ] ; then
EUID=`id -u`
diff --git a/tests/f2fs_test.in b/tests/f2fs_test.in
index 85f8cc8bc..0896810ea 100644
--- a/tests/f2fs_test.in
+++ b/tests/f2fs_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if [ "x$EUID" = "x" ] ; then
EUID=`id -u`
diff --git a/tests/fat_test.in b/tests/fat_test.in
index 8a2b37c5c..966b2c9db 100644
--- a/tests/fat_test.in
+++ b/tests/fat_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if [ "x$EUID" = "x" ] ; then
EUID=`id -u`
diff --git a/tests/fddboot_test.in b/tests/fddboot_test.in
index 6ef49efcb..74d2bcd87 100644
--- a/tests/fddboot_test.in
+++ b/tests/fddboot_test.in
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with GRUB. If not, see .
-set -e
+set -ex
grubshell=@builddir@/grub-shell
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/file_filter/file.gz.sig b/tests/file_filter/file.gz.sig
index 602e6187e..27cdcff2d 100644
Binary files a/tests/file_filter/file.gz.sig and b/tests/file_filter/file.gz.sig differ
diff --git a/tests/file_filter/file.lzop.sig b/tests/file_filter/file.lzop.sig
index 7c68dcf93..05949ae72 100644
Binary files a/tests/file_filter/file.lzop.sig and b/tests/file_filter/file.lzop.sig differ
diff --git a/tests/file_filter/file.xz.sig b/tests/file_filter/file.xz.sig
index 57569242e..9633a2fa1 100644
Binary files a/tests/file_filter/file.xz.sig and b/tests/file_filter/file.xz.sig differ
diff --git a/tests/file_filter/file.zstd b/tests/file_filter/file.zstd
new file mode 100644
index 000000000..6ea24ccf8
Binary files /dev/null and b/tests/file_filter/file.zstd differ
diff --git a/tests/file_filter/file.zstd.sig b/tests/file_filter/file.zstd.sig
new file mode 100644
index 000000000..ab101bb6e
Binary files /dev/null and b/tests/file_filter/file.zstd.sig differ
diff --git a/tests/file_filter/keys b/tests/file_filter/keys
index 1afa71382..67f29b88e 100644
Binary files a/tests/file_filter/keys and b/tests/file_filter/keys differ
diff --git a/tests/file_filter/keys.pub b/tests/file_filter/keys.pub
index 61d4e7a7b..44418a6f4 100644
Binary files a/tests/file_filter/keys.pub and b/tests/file_filter/keys.pub differ
diff --git a/tests/file_filter/test.cfg b/tests/file_filter/test.cfg
index 4308aaca5..ec4d10ab4 100644
--- a/tests/file_filter/test.cfg
+++ b/tests/file_filter/test.cfg
@@ -3,4 +3,5 @@ set check_signatures=enforce
cat /file.gz
cat /file.xz
cat /file.lzop
+cat /file.zstd
set check_signatures=
diff --git a/tests/file_filter_test.in b/tests/file_filter_test.in
index ed6abcb5a..54fcf8e88 100644
--- a/tests/file_filter_test.in
+++ b/tests/file_filter_test.in
@@ -14,19 +14,20 @@
# You should have received a copy of the GNU General Public License
# along with GRUB. If not, see .
-set -e
+set -ex
grubshell=@builddir@/grub-shell
. "@builddir@/grub-core/modinfo.sh"
-filters="gzio xzio lzopio pgp"
+filters="gzio xzio lzopio zstdio pgp"
modules="cat mpi"
for mod in $(cut -d ' ' -f 2 "@builddir@/grub-core/crypto.lst" | sort -u); do
modules="$modules $mod"
done
-for file in file.gz file.xz file.lzop file.gz.sig file.xz.sig file.lzop.sig keys.pub; do
+for file in file.gz file.xz file.lzop file.zstd file.gz.sig file.xz.sig \
+ file.lzop.sig file.zstd.sig keys.pub; do
files="$files /$file=@srcdir@/tests/file_filter/$file"
done
@@ -35,6 +36,8 @@ result="Hello, user!
Hello, user!
+Hello, user!
+
Hello, user!"
out="$("${grubshell}" --modules="$modules $filters" --files="$files" "@srcdir@/tests/file_filter/test.cfg")"
diff --git a/tests/grub_cmd_cryptomount.in b/tests/grub_cmd_cryptomount.in
index eaa187efa..bcba7c74c 100644
--- a/tests/grub_cmd_cryptomount.in
+++ b/tests/grub_cmd_cryptomount.in
@@ -38,6 +38,7 @@ fi
COMMON_OPTS='${V:+--debug=$V} --cs-opts="--pbkdf-force-iterations 1000"'
debug=${GRUB_SHELL_DEFAULT_DEBUG:-$GRUB_TEST_DEFAULT_DEBUG}
+builddir="@builddir@"
_testcase() {
local EXPECTEDRES=$1
@@ -92,95 +93,94 @@ testcase_fail() { _testcase 1 "$@"; }
### LUKS1 tests
eval testcase "'LUKS1 test cryptsetup defaults:'" \
- @builddir@/grub-shell-luks-tester --luks=1 $COMMON_OPTS
+ "$builddir/grub-shell-luks-tester" --luks=1 $COMMON_OPTS
eval testcase "'LUKS1 test with twofish cipher:'" \
- @builddir@/grub-shell-luks-tester --luks=1 $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" --luks=1 $COMMON_OPTS \
"--cs-opts='--cipher twofish-xts-plain64'"
eval testcase "'LUKS1 test key file support:'" \
- @builddir@/grub-shell-luks-tester --luks=1 $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" --luks=1 $COMMON_OPTS \
--keyfile
eval testcase "'LUKS1 test key file with offset:'" \
- @builddir@/grub-shell-luks-tester --luks=1 $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" --luks=1 $COMMON_OPTS \
--keyfile --cs-opts="--keyfile-offset=237"
eval testcase "'LUKS1 test key file with offset and size:'" \
- @builddir@/grub-shell-luks-tester --luks=1 $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" --luks=1 $COMMON_OPTS \
--keyfile "--cs-opts='--keyfile-offset=237 --keyfile-size=1023'"
eval testcase "'LUKS1 test detached header support:'" \
- @builddir@/grub-shell-luks-tester --luks=1 $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" --luks=1 $COMMON_OPTS \
--detached-header
eval testcase "'LUKS1 test both detached header and key file:'" \
- @builddir@/grub-shell-luks-tester --luks=1 $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" --luks=1 $COMMON_OPTS \
--keyfile --detached-header
### LUKS2 tests (mirroring the LUKS1 tests above)
LUKS2_COMMON_OPTS="--luks=2 --cs-opts=--pbkdf=pbkdf2"
eval testcase "'LUKS2 test cryptsetup defaults:'" \
- @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS
+ "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS
eval testcase "'LUKS2 test with twofish cipher:'" \
- @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \
"--cs-opts='--cipher twofish-xts-plain64'"
eval testcase "'LUKS2 test key file support:'" \
- @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \
--keyfile
eval testcase "'LUKS2 test key file with offset:'" \
- @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \
--keyfile --cs-opts="--keyfile-offset=237"
eval testcase "'LUKS2 test key file with offset and size:'" \
- @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \
--keyfile "--cs-opts='--keyfile-offset=237 --keyfile-size=1023'"
eval testcase "'LUKS2 test detached header support:'" \
- @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \
--detached-header
eval testcase "'LUKS2 test both detached header and key file:'" \
- @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \
--keyfile --detached-header
### LUKS1 specific tests
# Tests for xts-plain and xts-plain64 modes
eval testcase "'LUKS1 test cryptsetup xts-plain:'" \
- @builddir@/grub-shell-luks-tester --luks=1 $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" --luks=1 $COMMON_OPTS \
"--cs-opts='--cipher aes-xts-plain'"
eval testcase "'LUKS1 test cryptsetup xts-plain64:'" \
- @builddir@/grub-shell-luks-tester --luks=1 $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" --luks=1 $COMMON_OPTS \
"--cs-opts='--cipher aes-xts-plain64'"
### LUKS2 specific tests
eval testcase "'LUKS2 test with 1k sector size:'" \
- @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \
"--cs-opts='--sector-size 1024'"
eval testcase "'LUKS2 test with 2k sector size:'" \
- @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \
"--cs-opts='--sector-size 2048'"
eval testcase "'LUKS2 test with 4k sector size:'" \
- @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \
"--cs-opts='--sector-size 4096'"
eval testcase "'LUKS2 test with non-default key slot:'" \
- @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \
"--cs-opts='--key-slot 5'"
eval testcase "'LUKS2 test with different metadata size:'" \
- @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \
"--cs-opts='--luks2-metadata-size 512k'"
-# TODO: Expect a failure with LUKS2 volumes with argon2 key derivation
-eval testcase_fail "'LUKS2 test with argon2 pbkdf:'" \
- @builddir@/grub-shell-luks-tester --luks=2 $COMMON_OPTS \
+eval testcase "'LUKS2 test with argon2 pbkdf:'" \
+ "$builddir/grub-shell-luks-tester" --luks=2 $COMMON_OPTS \
"--cs-opts='--pbkdf-memory 32'" "--cs-opts='--pbkdf-parallel 1'"
# Add good password to second slot and change first slot to unchecked password
@@ -192,7 +192,7 @@ cat >$csscript <<'EOF'
EOF
eval testcase "'LUKS2 test with second key slot and first slot using different password:'" \
- @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \
+ "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \
"--cs-script='$csscript'"
test -n "$debug" || rm "$csscript"
diff --git a/tests/grub_cmd_date.in b/tests/grub_cmd_date.in
index 4903ad6cc..359c624fc 100644
--- a/tests/grub_cmd_date.in
+++ b/tests/grub_cmd_date.in
@@ -1,5 +1,5 @@
#! @BUILD_SHEBANG@
-set -e
+set -ex
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/grub_cmd_regexp.in b/tests/grub_cmd_regexp.in
index 6520bd6d7..13633bb05 100644
--- a/tests/grub_cmd_regexp.in
+++ b/tests/grub_cmd_regexp.in
@@ -1,5 +1,5 @@
#! @BUILD_SHEBANG@
-set -e
+set -ex
# Run GRUB script in a Qemu instance
# Copyright (C) 2010 Free Software Foundation, Inc.
diff --git a/tests/grub_cmd_set_date.in b/tests/grub_cmd_set_date.in
index 17673cd8a..5572535fa 100644
--- a/tests/grub_cmd_set_date.in
+++ b/tests/grub_cmd_set_date.in
@@ -1,5 +1,5 @@
#! @BUILD_SHEBANG@
-set -e
+set -ex
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/grub_cmd_sleep.in b/tests/grub_cmd_sleep.in
index 1a57fb388..2da9d722e 100644
--- a/tests/grub_cmd_sleep.in
+++ b/tests/grub_cmd_sleep.in
@@ -1,5 +1,5 @@
#! @BUILD_SHEBANG@
-set -e
+set -ex
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/grub_cmd_test.in b/tests/grub_cmd_test.in
index 043c3a634..a78a5d6ee 100644
--- a/tests/grub_cmd_test.in
+++ b/tests/grub_cmd_test.in
@@ -1,5 +1,5 @@
#! @BUILD_SHEBANG@
-set -e
+set -ex
# create a randome file
empty="`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"`" || exit 99
diff --git a/tests/grub_func_test.in b/tests/grub_func_test.in
index 1fa3c4352..6065ff6df 100644
--- a/tests/grub_func_test.in
+++ b/tests/grub_func_test.in
@@ -1,5 +1,5 @@
#! @BUILD_SHEBANG@
-set -e
+set -ex
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/grub_script_blanklines.in b/tests/grub_script_blanklines.in
index bd8735491..9b84782c3 100644
--- a/tests/grub_script_blanklines.in
+++ b/tests/grub_script_blanklines.in
@@ -1,5 +1,5 @@
#! @BUILD_SHEBANG@
-set -e
+set -ex
@builddir@/grub-script-check <.
-set -e
+set -ex
grubshell=@builddir@/grub-shell
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/hddboot_test.in b/tests/hddboot_test.in
index 764e0da1c..76704c379 100644
--- a/tests/hddboot_test.in
+++ b/tests/hddboot_test.in
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with GRUB. If not, see .
-set -e
+set -ex
grubshell=@builddir@/grub-shell
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/help_test.in b/tests/help_test.in
index 9c8ca52c8..ca3d7f31b 100644
--- a/tests/help_test.in
+++ b/tests/help_test.in
@@ -1,5 +1,5 @@
#! @BUILD_SHEBANG@
-set -e
+set -ex
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/hfs_test.in b/tests/hfs_test.in
index 960f1cbd0..99432114f 100644
--- a/tests/hfs_test.in
+++ b/tests/hfs_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if [ "x$EUID" = "x" ] ; then
EUID=`id -u`
diff --git a/tests/hfsplus_test.in b/tests/hfsplus_test.in
index f727cf0e2..402d1cd66 100644
--- a/tests/hfsplus_test.in
+++ b/tests/hfsplus_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if [ "x$EUID" = "x" ] ; then
EUID=`id -u`
diff --git a/tests/iso9660_test.in b/tests/iso9660_test.in
index a1f752adf..a36f3022a 100644
--- a/tests/iso9660_test.in
+++ b/tests/iso9660_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if ! which xorriso >/dev/null 2>&1; then
echo "xorriso not installed; cannot test iso9660."
diff --git a/tests/jfs_test.in b/tests/jfs_test.in
index d13780e23..07f1ca193 100644
--- a/tests/jfs_test.in
+++ b/tests/jfs_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if [ "x$EUID" = "x" ] ; then
EUID=`id -u`
diff --git a/tests/luks1_test.in b/tests/luks1_test.in
index cd28fd714..1df4a2f5f 100644
--- a/tests/luks1_test.in
+++ b/tests/luks1_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if [ "x$EUID" = "x" ] ; then
EUID=`id -u`
diff --git a/tests/luks2_test.in b/tests/luks2_test.in
index 6a26ba626..75ac2c191 100644
--- a/tests/luks2_test.in
+++ b/tests/luks2_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if [ "x$EUID" = "x" ] ; then
EUID=`id -u`
diff --git a/tests/lzocompress_test.in b/tests/lzocompress_test.in
index 915f74bd9..a89d014e9 100644
--- a/tests/lzocompress_test.in
+++ b/tests/lzocompress_test.in
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with GRUB. If not, see .
-set -e
+set -ex
grubshell=@builddir@/grub-shell
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/minixfs_test.in b/tests/minixfs_test.in
index c62f56c8b..780f699fa 100644
--- a/tests/minixfs_test.in
+++ b/tests/minixfs_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if [ "x$EUID" = "x" ] ; then
EUID=`id -u`
diff --git a/tests/netboot_test.in b/tests/netboot_test.in
index 510c9c34b..b8a5f9abb 100644
--- a/tests/netboot_test.in
+++ b/tests/netboot_test.in
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with GRUB. If not, see .
-set -e
+set -ex
grubshell=@builddir@/grub-shell
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/nilfs2_test.in b/tests/nilfs2_test.in
index 8cc93754c..95ec7c3b4 100644
--- a/tests/nilfs2_test.in
+++ b/tests/nilfs2_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if [ "x$EUID" = "x" ] ; then
EUID=`id -u`
diff --git a/tests/ntfs_test.in b/tests/ntfs_test.in
index c2b08d27f..b6432d2a1 100644
--- a/tests/ntfs_test.in
+++ b/tests/ntfs_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if [ "x$EUID" = "x" ] ; then
EUID=`id -u`
diff --git a/tests/ohci_test.in b/tests/ohci_test.in
index a40d3bc0a..1652b7efc 100644
--- a/tests/ohci_test.in
+++ b/tests/ohci_test.in
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with GRUB. If not, see .
-set -e
+set -ex
grubshell=@builddir@/grub-shell
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/partmap_test.in b/tests/partmap_test.in
index 4138e88fe..6157a8537 100644
--- a/tests/partmap_test.in
+++ b/tests/partmap_test.in
@@ -1,5 +1,5 @@
#! @BUILD_SHEBANG@
-set -e
+set -ex
# Copyright (C) 2010 Free Software Foundation, Inc.
#
diff --git a/tests/pata_test.in b/tests/pata_test.in
index 4d0e7d573..6c2fa43dd 100644
--- a/tests/pata_test.in
+++ b/tests/pata_test.in
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with GRUB. If not, see .
-set -e
+set -ex
grubshell=@builddir@/grub-shell
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/printf_unit_test.c b/tests/printf_unit_test.c
index 098c29fd9..f2de187b0 100644
--- a/tests/printf_unit_test.c
+++ b/tests/printf_unit_test.c
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
#define MSG "printf test failed: %s, %s", real, expected
@@ -73,6 +74,15 @@ printf_test (void)
grub_snprintf (real, sizeof (real), "%%0%dd ", 1);
snprintf (expected, sizeof (expected), "%%0%dd ", 1);
grub_test_assert (strcmp (real, expected) == 0, MSG);
+ grub_snprintf (real, sizeof (real), "%zd %zd %zd", (ssize_t) -1, (ssize_t) (SIZE_MAX >> 1), (ssize_t) 42);
+ snprintf (expected, sizeof (expected), "%zd %zd %zd", (ssize_t) -1, (ssize_t) (SIZE_MAX >> 1), (ssize_t) 42);
+ grub_test_assert (strcmp (real, expected) == 0, MSG);
+ grub_snprintf (real, sizeof (real), "%zu %zu %zu", (size_t) 0, (size_t) SIZE_MAX, (size_t) 42);
+ snprintf (expected, sizeof (expected), "%zu %zu %zu", (size_t) 0, (size_t) SIZE_MAX, (size_t) 42);
+ grub_test_assert (strcmp (real, expected) == 0, MSG);
+ grub_snprintf (real, sizeof (real), "%zx %zx %zx", (ssize_t) (SIZE_MAX >> 1), (size_t) SIZE_MAX, (size_t) 0xdeadbeefU);
+ snprintf (expected, sizeof (expected), "%zx %zx %zx", (ssize_t) (SIZE_MAX >> 1), (size_t) SIZE_MAX, (size_t) 0xdeadbeefU);
+ grub_test_assert (strcmp (real, expected) == 0, MSG);
}
GRUB_UNIT_TEST ("printf_unit_test", printf_test);
diff --git a/tests/pseries_test.in b/tests/pseries_test.in
index 9b4090cf5..a1dd1670a 100644
--- a/tests/pseries_test.in
+++ b/tests/pseries_test.in
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with GRUB. If not, see .
-set -e
+set -ex
grubshell=@builddir@/grub-shell
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/reiserfs_test.in b/tests/reiserfs_test.in
index 37226c01b..6f8ebcf29 100644
--- a/tests/reiserfs_test.in
+++ b/tests/reiserfs_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if [ "x$EUID" = "x" ] ; then
EUID=`id -u`
diff --git a/tests/romfs_test.in b/tests/romfs_test.in
index f968e9b7d..e3b4a0210 100644
--- a/tests/romfs_test.in
+++ b/tests/romfs_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if ! which genromfs >/dev/null 2>&1; then
echo "genromfs not installed; cannot test romfs."
diff --git a/tests/serial_test.in b/tests/serial_test.in
index 48655d4b9..331c3c3ad 100644
--- a/tests/serial_test.in
+++ b/tests/serial_test.in
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with GRUB. If not, see .
-set -e
+set -ex
grubshell=@builddir@/grub-shell
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/squashfs_test.in b/tests/squashfs_test.in
index 15e70218f..36f8e9735 100644
--- a/tests/squashfs_test.in
+++ b/tests/squashfs_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if ! which mksquashfs >/dev/null 2>&1; then
echo "mksquashfs not installed; cannot test squashfs."
diff --git a/tests/syslinux_test.in b/tests/syslinux_test.in
index 44d3cdf7a..e81c15310 100644
--- a/tests/syslinux_test.in
+++ b/tests/syslinux_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
outfile="`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"`" || exit 99
diff --git a/tests/tar_test.in b/tests/tar_test.in
index 97944b243..4f0c697ac 100644
--- a/tests/tar_test.in
+++ b/tests/tar_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if ! which tar >/dev/null 2>&1; then
echo "tar not installed; cannot test tar."
diff --git a/tests/test_sha512sum.in b/tests/test_sha512sum.in
index b2bd89609..3866f40c8 100644
--- a/tests/test_sha512sum.in
+++ b/tests/test_sha512sum.in
@@ -1,5 +1,5 @@
#! @BUILD_SHEBANG@
-set -e
+set -ex
# create a randome file
file="`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"`" || exit 99
diff --git a/tests/tpm2_key_protector_test.in b/tests/tpm2_key_protector_test.in
index 1d80d5d26..5dd86d6ee 100644
--- a/tests/tpm2_key_protector_test.in
+++ b/tests/tpm2_key_protector_test.in
@@ -304,6 +304,58 @@ EOF
fi
}
+tpm2_seal_unseal_cap() {
+ pcr_bank="sha256"
+
+ original_pcr1="$(tpm2_pcrread ${pcr_bank}:1) | tail -1 | cut -d' ' -f7"
+
+ grub_cfg=${tpm2testdir}/testcase.cfg
+
+ # Seal the password with grub-protect
+ grub-protect \
+ --tpm2-device="${tpm2dev}" \
+ --action=add \
+ --protector=tpm2 \
+ --tpm2key \
+ --tpm2-bank="${pcr_bank}" \
+ --tpm2-pcrs=0,1 \
+ --tpm2-keyfile="${lukskeyfile}" \
+ --tpm2-outfile="${sealedkey}" || ret=$?
+ if [ "${ret}" -ne 0 ]; then
+ echo "Failed to seal the secret key: ${ret}" >&2
+ return 99
+ fi
+
+ # Write the TPM unsealing script and cap PCR 1
+ cat > "${grub_cfg}" < "${testoutput}" || ret=$?
+
+ if [ "${ret}" -eq 0 ]; then
+ if ! grep -q "^${vtext}$" "${testoutput}"; then
+ echo "error: test not verified [`cat ${testoutput}`]" >&2
+ return 1
+ fi
+ else
+ echo "grub-emu exited with error: ${ret}" >&2
+ return 99
+ fi
+
+ capped_pcr1="$(tpm2_pcrread ${pcr_bank}:1) | tail -1 | cut -d' ' -f7"
+
+ if [ "${original_pcr1}" = "${capped_pcr1}" ]; then
+ echo "error: PCR 1 not capped" >&2
+ return 1
+ fi
+}
+
# Testcases for SRK mode
declare -a srktests=()
srktests+=("default transient no_fallback_srk sha256")
@@ -357,4 +409,17 @@ for i in "${!nvtests[@]}"; do
fi
done
+# Testcase for PCR Capping
+tpm2_seal_unseal_cap || ret=$?
+if [ "${ret}" -eq 0 ]; then
+ echo "TPM2 [PCR Capping]: PASS"
+elif [ "${ret}" -eq 1 ]; then
+ echo "TPM2 [PCR Capping]: FAIL"
+ ret=0
+ exit_status=1
+else
+ echo "Unexpected failure [PCR Capping]" >&2
+ exit ${ret}
+fi
+
exit ${exit_status}
diff --git a/tests/udf_test.in b/tests/udf_test.in
index 302b28ab2..0723b3be0 100644
--- a/tests/udf_test.in
+++ b/tests/udf_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if [ "x$EUID" = "x" ] ; then
EUID=`id -u`
diff --git a/tests/uhci_test.in b/tests/uhci_test.in
index de199a281..46de98d92 100644
--- a/tests/uhci_test.in
+++ b/tests/uhci_test.in
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with GRUB. If not, see .
-set -e
+set -ex
grubshell=@builddir@/grub-shell
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/util/grub-fs-tester.in b/tests/util/grub-fs-tester.in
index cac58dafa..00ecec015 100644
--- a/tests/util/grub-fs-tester.in
+++ b/tests/util/grub-fs-tester.in
@@ -386,9 +386,12 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
FSLABEL="g;/_é䏌䐓䏕䎛䎾䏴кит u"
#FSLABEL="g;/_é莭莽😁кит u"
;;
- # FS LIMITATION: reiserfs, extN, jfs and erofs label is at most 16 UTF-8 characters
- x"reiserfs_old" | x"reiserfs" | x"ext"* | x"lvm"* | x"luks"* | x"mdraid"* | x"jfs" | x"jfs_caseins" | x"erofs_"*)
+ # FS LIMITATION: reiserfs, extN, and jfs label is at most 16 UTF-8 characters
+ x"reiserfs_old" | x"reiserfs" | x"ext"* | x"lvm"* | x"luks"* | x"mdraid"* | x"jfs" | x"jfs_caseins")
FSLABEL="g;/éт 莭😁";;
+ # FS LIMITATION: erofs label is at most 15 UTF-8 characters
+ x"erofs_"*)
+ FSLABEL="g;/é 莭😁";;
# FS LIMITATION: No underscore, space, semicolon, slash or international characters in UFS* in label. Limited to 32 UTF-8 characters
x"ufs1" | x"ufs1_sun" | x"ufs2")
FSLABEL="grubtest""ieurrucnenreeiurueurewf";;
@@ -587,6 +590,10 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
# FS LIMITATION: romfs has no timestamps.
x"romfs")
NOFILETIME=y; NOFSTIME=y;;
+ # FS LIMITATION: Compact inodes do not allow for modification
+ # times that are different from FS creation times.
+ x"erofs_compact")
+ NOFILETIME=y;;
esac
NOFSLABEL=n
@@ -596,11 +603,12 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
| x"minix3" | xreiserfs_old)
NOFSLABEL=y;;
x"erofs_"*)
- MKFS_EROFS_VERSION=$(mkfs.erofs -V 2>/dev/null | tr ' ' '\n' | grep '^[0-9]')
+ MKFS_EROFS_VERSION=$(mkfs.erofs 2>/dev/null | head -n 1 | (read _ V; echo $V))
# check if the version is at least 1.6
- if [ $(sort -V <(echo "$MKFS_EROFS_VERSION") <(echo "1.6") | head -n 1) != "1.6" ]; then
+ if [ "$(echo -e "${MKFS_EROFS_VERSION}\n1.6" | sort -V | head -n 1)" != "1.6" ]; then
NOFSLABEL=y
- fi
+ unset FSLABEL
+ fi;;
esac
PDIRCOMPNUM=210
@@ -735,7 +743,9 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
dd if=/dev/urandom of="${MOUNTDEVICE}" bs=1 seek=$((0x468)) conv=notrunc count=8
MOUNTFS="hfsplus";;
x"hfs")
- "mkfs.hfs" -b $BLKSIZE -v "`echo $FSLABEL |recode utf8..macroman`" -h "${MOUNTDEVICE}"
+ # CSMACINTOSH is an alias for MacRoman which is the
+ # encoding used on HFS.
+ "mkfs.hfs" -b $BLKSIZE -v "`echo $FSLABEL | recode utf8..CSMACINTOSH`" -h "${MOUNTDEVICE}"
dd if=/dev/urandom of="${MOUNTDEVICE}" bs=1 seek=$((0x474)) conv=notrunc count=8
MOUNTOPTS="iocharset=utf8,codepage=macroman,"
;;
@@ -860,8 +870,14 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
MOUNTDEVICE="/dev/mapper/grub_test-testvol"
MOUNTFS=ext2
"mkfs.ext2" -L "$FSLABEL" -q "${MOUNTDEVICE}" ;;
- x"luks"*)
- echo -n "$PASS" | cryptsetup luksFormat --type "$fs" --sector-size $SECSIZE --pbkdf pbkdf2 --force-password --disable-locks $LODEVICE
+ xluks1)
+ echo -n "$PASS" | cryptsetup luksFormat --type luks1 --sector-size $SECSIZE --pbkdf pbkdf2 --force-password --disable-locks $LODEVICE
+ echo -n "$PASS" | cryptsetup open --disable-locks $LODEVICE "$DMNAME"
+ MOUNTDEVICE="/dev/mapper/${DMNAME}"
+ MOUNTFS=ext2
+ "mkfs.ext2" -L "$FSLABEL" -q "${MOUNTDEVICE}" ;;
+ xluks2)
+ echo -n "$PASS" | cryptsetup luksFormat --type luks2 --sector-size $SECSIZE --pbkdf argon2id --force-password --disable-locks $LODEVICE
echo -n "$PASS" | cryptsetup open --disable-locks $LODEVICE "$DMNAME"
MOUNTDEVICE="/dev/mapper/${DMNAME}"
MOUNTFS=ext2
@@ -871,7 +887,11 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
xnilfs2)
"mkfs.nilfs2" -L "$FSLABEL" -b $BLKSIZE -q "${MOUNTDEVICE}" ;;
xext2_old)
- MKE2FS_DEVICE_SECTSIZE=$SECSIZE "mkfs.ext2" -r 0 -b $BLKSIZE -L "$FSLABEL" -q "${MOUNTDEVICE}"
+ if "mkfs.ext2" -r 0 2>&1 | grep -q -F "the -r option has been removed"; then
+ MKE2FS_DEVICE_SECTSIZE=$SECSIZE "mkfs.ext2" -E revision=0 -b $BLKSIZE -L "$FSLABEL" -q "${MOUNTDEVICE}"
+ else
+ MKE2FS_DEVICE_SECTSIZE=$SECSIZE "mkfs.ext2" -r 0 -b $BLKSIZE -L "$FSLABEL" -q "${MOUNTDEVICE}"
+ fi
MOUNTFS=ext2
;;
xext4_metabg)
@@ -1484,6 +1504,12 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
# With some abstractions like mdraid flushing to disk
# may be delayed for a long time.
FSTIME="$UMOUNT_TIME";;
+ x"erofs_"*)
+ # Creating the erofs image may take more than a few
+ # seconds. Use the more accurate timestamp from the
+ # superblock.
+ FSTIME="$(dump.erofs -s "${FSIMAGEP}0.img" | grep ^"Filesystem created:" | (read _ _ REST; echo $REST) )"
+ FSTIME="$(date -d "$FSTIME" -u '+%Y-%m-%d %H:%M:%S')";;
xsquash*)
# Creating the squash image may take more than a few
# seconds. Use the more accurate timestamp from the
diff --git a/tests/xfs_test.in b/tests/xfs_test.in
index 5e029c182..2c9e6e348 100644
--- a/tests/xfs_test.in
+++ b/tests/xfs_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if [ "x$EUID" = "x" ] ; then
EUID=`id -u`
diff --git a/tests/xzcompress_test.in b/tests/xzcompress_test.in
index 6ef73e41e..62f0564db 100644
--- a/tests/xzcompress_test.in
+++ b/tests/xzcompress_test.in
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with GRUB. If not, see .
-set -e
+set -ex
grubshell=@builddir@/grub-shell
. "@builddir@/grub-core/modinfo.sh"
diff --git a/tests/zfs_test.in b/tests/zfs_test.in
index 0d0a57f7d..462a9b95f 100644
--- a/tests/zfs_test.in
+++ b/tests/zfs_test.in
@@ -1,6 +1,6 @@
#!@BUILD_SHEBANG@
-set -e
+set -ex
if [ "x$EUID" = "x" ] ; then
EUID=`id -u`
@@ -19,7 +19,6 @@ fi
"@builddir@/grub-fs-tester" zfs_lzjb
"@builddir@/grub-fs-tester" zfs_gzip
"@builddir@/grub-fs-tester" zfs_zle
-"@builddir@/grub-fs-tester" zfs_zstd
"@builddir@/grub-fs-tester" zfs_raidz3
"@builddir@/grub-fs-tester" zfs_raidz2
"@builddir@/grub-fs-tester" zfs_raidz
diff --git a/tests/zfs_zstd_test.in b/tests/zfs_zstd_test.in
new file mode 100644
index 000000000..8cafceaaf
--- /dev/null
+++ b/tests/zfs_zstd_test.in
@@ -0,0 +1,30 @@
+#!@BUILD_SHEBANG@
+
+set -e
+
+if [ "x$EUID" = "x" ] ; then
+ EUID=`id -u`
+fi
+
+if [ "$EUID" != 0 ] ; then
+ exit 99
+fi
+
+if ! which zpool >/dev/null 2>&1; then
+ echo "zpool not installed; cannot test zfs."
+ exit 99
+fi
+
+if ! which zfs >/dev/null 2>&1; then
+ echo "zfs not installed; cannot test zfs."
+ exit 99
+fi
+
+# If ZFS ZSTD compression is not supported (as is the case with zfs-fuse
+# for example at the time of writing) then fail early the ZSTD compression testing.
+if ! zfs get 2>&1 | grep -q "compression.*zstd"; then
+ echo "zfs zstd compression not supported; cannot test zfs zstd."
+ exit 99
+fi
+
+"@builddir@/grub-fs-tester" zfs_zstd
diff --git a/util/bash-completion.d/Makefile.am b/util/bash-completion.d/Makefile.am
index 33fff9546..ede14afcf 100644
--- a/util/bash-completion.d/Makefile.am
+++ b/util/bash-completion.d/Makefile.am
@@ -50,7 +50,7 @@ CLEANFILES = $(bash_completion_script) \
$(grub_mkfont_script) \
$(grub_mkimage_script) \
$(grub_mkpasswd_pbkdf2_script) \
- $(grub_mkrescure_script) \
+ $(grub_mkrescue_script) \
$(grub_probe_script) \
$(grub_reboot_script) \
$(grub_script_check_script) \
@@ -67,7 +67,7 @@ bashcompletion_DATA = $(bash_completion_script) \
$(grub_mkfont_script) \
$(grub_mkimage_script) \
$(grub_mkpasswd_pbkdf2_script) \
- $(grub_mkrescure_script) \
+ $(grub_mkrescue_script) \
$(grub_probe_script) \
$(grub_reboot_script) \
$(grub_script_check_script) \
diff --git a/util/grub-editenv.c b/util/grub-editenv.c
index db6f187cc..41bb86739 100644
--- a/util/grub-editenv.c
+++ b/util/grub-editenv.c
@@ -23,8 +23,11 @@
#include
#include
#include
-#include
+#include
#include
+#include
+#include
+#include
#include
#include
@@ -120,6 +123,186 @@ block, use `rm %s'."),
NULL, help_filter, NULL
};
+struct fs_envblk_spec {
+ const char *fs_name;
+ off_t offset;
+ size_t size;
+};
+typedef struct fs_envblk_spec fs_envblk_spec_t;
+
+static grub_envblk_t fs_envblk_open (grub_envblk_t envblk);
+static void fs_envblk_write (grub_envblk_t envblk);
+
+struct fs_envblk_ops {
+ grub_envblk_t (*open) (grub_envblk_t);
+ void (*write) (grub_envblk_t);
+};
+typedef struct fs_envblk_ops fs_envblk_ops_t;
+
+struct fs_envblk {
+ fs_envblk_spec_t *spec;
+ fs_envblk_ops_t *ops;
+ const char *dev;
+};
+typedef struct fs_envblk *fs_envblk_t;
+
+static fs_envblk_ops_t fs_envblk_ops = {
+ .open = fs_envblk_open,
+ .write = fs_envblk_write
+};
+
+/*
+ * fs_envblk_spec describes the file-system specific layout of reserved raw
+ * blocks used as environment blocks. At present only Btrfs is supported. Other
+ * file-systems may be added if they provide a similar facility and avoid the
+ * limitation of writing to COW.
+ *
+ * Note: If this table is modified, also update
+ * grub-core/fs/btrfs.c::btrfs_head, which defines the layout in the Btrfs
+ * header and exports GRUB_ENV_BTRFS_OFFSET, so that both stay consistent.
+ */
+static fs_envblk_spec_t fs_envblk_spec[] = {
+ { "btrfs", GRUB_ENV_BTRFS_OFFSET, GRUB_DISK_SECTOR_SIZE },
+ { NULL, 0, 0 }
+};
+
+static fs_envblk_t fs_envblk = NULL;
+
+static void
+fs_envblk_init (const char *fs_name, const char *dev)
+{
+ fs_envblk_spec_t *p;
+
+ if (fs_name == NULL || dev == NULL)
+ return;
+
+ for (p = fs_envblk_spec; p->fs_name != NULL; p++)
+ {
+ if (strcmp (fs_name, p->fs_name) == 0)
+ {
+ if (fs_envblk == NULL)
+ fs_envblk = xmalloc (sizeof (*fs_envblk));
+ fs_envblk->spec = p;
+ fs_envblk->dev = xstrdup (dev);
+ fs_envblk->ops = &fs_envblk_ops;
+ break;
+ }
+ }
+}
+
+static int
+read_env_block_var (const char *varname, const char *value, void *hook_data)
+{
+ grub_envblk_t *p_envblk = (grub_envblk_t *) hook_data;
+ off_t off;
+ size_t sz;
+ char *p, *buf;
+ FILE *fp;
+
+ if (p_envblk == NULL || fs_envblk == NULL)
+ return 1;
+
+ if (strcmp (varname, "env_block") != 0)
+ return 0;
+
+ off = strtol (value, &p, 10);
+ if (*p == '+')
+ sz = strtol (p + 1, &p, 10);
+ else
+ return 0;
+
+ if (*p != '\0' || sz == 0)
+ return 0;
+
+ off <<= GRUB_DISK_SECTOR_BITS;
+ sz <<= GRUB_DISK_SECTOR_BITS;
+
+ fp = grub_util_fopen (fs_envblk->dev, "rb");
+ if (fp == NULL)
+ grub_util_error (_("cannot open `%s': %s"), fs_envblk->dev, strerror (errno));
+
+ if (fseek (fp, off, SEEK_SET) < 0)
+ grub_util_error (_("cannot seek `%s': %s"), fs_envblk->dev, strerror (errno));
+
+ buf = xmalloc (sz);
+ if ((fread (buf, 1, sz, fp)) != sz)
+ grub_util_error (_("cannot read `%s': %s"), fs_envblk->dev, strerror (errno));
+
+ fclose (fp);
+
+ *p_envblk = grub_envblk_open (buf, sz);
+
+ return 1;
+}
+
+static void
+create_env_on_block (void)
+{
+ FILE *fp;
+ char *buf;
+ const char *device;
+ off_t offset;
+ size_t size;
+
+ if (fs_envblk == NULL)
+ return;
+
+ device = fs_envblk->dev;
+ offset = fs_envblk->spec->offset;
+ size = fs_envblk->spec->size;
+
+ fp = grub_util_fopen (device, "r+b");
+ if (fp == NULL)
+ grub_util_error (_("cannot open `%s': %s"), device, strerror (errno));
+
+ buf = xmalloc (size);
+ memcpy (buf, GRUB_ENVBLK_SIGNATURE, sizeof (GRUB_ENVBLK_SIGNATURE) - 1);
+ memset (buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1, '#', size - sizeof (GRUB_ENVBLK_SIGNATURE) + 1);
+
+ if (fseek (fp, offset, SEEK_SET) < 0)
+ grub_util_error (_("cannot seek `%s': %s"), device, strerror (errno));
+
+ if (fwrite (buf, 1, size, fp) != size)
+ grub_util_error (_("cannot write to `%s': %s"), device, strerror (errno));
+
+ grub_util_file_sync (fp);
+ free (buf);
+ fclose (fp);
+}
+
+static grub_envblk_t
+fs_envblk_open (grub_envblk_t envblk)
+{
+ grub_envblk_t envblk_on_block = NULL;
+ char *val;
+ off_t offset;
+ size_t size;
+
+ if (envblk == NULL)
+ return NULL;
+
+ offset = fs_envblk->spec->offset;
+ size = fs_envblk->spec->size;
+
+ grub_envblk_iterate (envblk, &envblk_on_block, read_env_block_var);
+
+ if (envblk_on_block != NULL && grub_envblk_size (envblk_on_block) == size)
+ return envblk_on_block;
+
+ create_env_on_block ();
+
+ offset = offset >> GRUB_DISK_SECTOR_BITS;
+ size = (size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS;
+
+ val = xasprintf ("%lld+%zu", (long long) offset, size);
+ if (grub_envblk_set (envblk, "env_block", val) == 0)
+ grub_util_error ("%s", _("environment block too small"));
+ grub_envblk_iterate (envblk, &envblk_on_block, read_env_block_var);
+ free (val);
+
+ return envblk_on_block;
+}
+
static grub_envblk_t
open_envblk_file (const char *name)
{
@@ -182,10 +365,17 @@ static void
list_variables (const char *name)
{
grub_envblk_t envblk;
+ grub_envblk_t envblk_on_block = NULL;
envblk = open_envblk_file (name);
+ grub_envblk_iterate (envblk, &envblk_on_block, read_env_block_var);
grub_envblk_iterate (envblk, NULL, print_var);
grub_envblk_close (envblk);
+ if (envblk_on_block != NULL)
+ {
+ grub_envblk_iterate (envblk_on_block, NULL, print_var);
+ grub_envblk_close (envblk_on_block);
+ }
}
static void
@@ -208,12 +398,68 @@ write_envblk (const char *name, grub_envblk_t envblk)
fclose (fp);
}
+static void
+fs_envblk_write (grub_envblk_t envblk)
+{
+ FILE *fp;
+ const char *device;
+ off_t offset;
+ size_t size;
+
+ if (envblk == NULL)
+ return;
+
+ device = fs_envblk->dev;
+ offset = fs_envblk->spec->offset;
+ size = fs_envblk->spec->size;
+
+ if (grub_envblk_size (envblk) > size)
+ grub_util_error ("%s", _("environment block too small"));
+
+ fp = grub_util_fopen (device, "r+b");
+
+ if (fp == NULL)
+ grub_util_error (_("cannot open `%s': %s"), device, strerror (errno));
+
+ if (fseek (fp, offset, SEEK_SET) < 0)
+ grub_util_error (_("cannot seek `%s': %s"), device, strerror (errno));
+
+ if (fwrite (grub_envblk_buffer (envblk), 1, grub_envblk_size (envblk), fp) != grub_envblk_size (envblk))
+ grub_util_error (_("cannot write to `%s': %s"), device, strerror (errno));
+
+ grub_util_file_sync (fp);
+ fclose (fp);
+}
+
+struct var_lookup_ctx {
+ const char *varname;
+ bool found;
+};
+typedef struct var_lookup_ctx var_lookup_ctx_t;
+
+static int
+var_lookup_iter (const char *varname, const char *value __attribute__ ((unused)), void *hook_data)
+{
+ var_lookup_ctx_t *ctx = (var_lookup_ctx_t *) hook_data;
+
+ if (grub_strcmp (ctx->varname, varname) == 0)
+ {
+ ctx->found = true;
+ return 1;
+ }
+ return 0;
+}
+
static void
set_variables (const char *name, int argc, char *argv[])
{
grub_envblk_t envblk;
+ grub_envblk_t envblk_on_block = NULL;
envblk = open_envblk_file (name);
+ if (fs_envblk != NULL)
+ envblk_on_block = fs_envblk->ops->open (envblk);
+
while (argc)
{
char *p;
@@ -224,33 +470,183 @@ set_variables (const char *name, int argc, char *argv[])
*(p++) = 0;
- if (! grub_envblk_set (envblk, argv[0], p))
- grub_util_error ("%s", _("environment block too small"));
+ if ((strcmp (argv[0], "next_entry") == 0) && envblk_on_block != NULL)
+ {
+ if (grub_envblk_set (envblk_on_block, argv[0], p) == 0)
+ grub_util_error ("%s", _("environment block too small"));
+ goto next;
+ }
+ if (strcmp (argv[0], "env_block") == 0)
+ {
+ grub_util_warn (_("can't set env_block as it's read-only"));
+ goto next;
+ }
+
+ if (grub_envblk_set (envblk, argv[0], p) == 0)
+ grub_util_error ("%s", _("environment block too small"));
+
+ if (envblk_on_block != NULL)
+ {
+ var_lookup_ctx_t ctx = {
+ .varname = argv[0],
+ .found = false
+ };
+
+ grub_envblk_iterate (envblk_on_block, &ctx, var_lookup_iter);
+ if (ctx.found == true)
+ grub_envblk_delete (envblk_on_block, argv[0]);
+ }
+ next:
argc--;
argv++;
}
write_envblk (name, envblk);
grub_envblk_close (envblk);
+
+ if (envblk_on_block != NULL)
+ {
+ fs_envblk->ops->write (envblk_on_block);
+ grub_envblk_close (envblk_on_block);
+ }
}
static void
unset_variables (const char *name, int argc, char *argv[])
{
grub_envblk_t envblk;
+ grub_envblk_t envblk_on_block = NULL;
envblk = open_envblk_file (name);
+
+ if (fs_envblk != NULL)
+ envblk_on_block = fs_envblk->ops->open (envblk);
+
while (argc)
{
grub_envblk_delete (envblk, argv[0]);
+ if (envblk_on_block != NULL)
+ grub_envblk_delete (envblk_on_block, argv[0]);
+
argc--;
argv++;
}
write_envblk (name, envblk);
grub_envblk_close (envblk);
+
+ if (envblk_on_block != NULL)
+ {
+ fs_envblk->ops->write (envblk_on_block);
+ grub_envblk_close (envblk_on_block);
+ }
+}
+
+static bool
+is_abstraction (grub_device_t dev)
+{
+ if (dev == NULL || dev->disk == NULL)
+ return false;
+
+ if (dev->disk->dev->id == GRUB_DISK_DEVICE_DISKFILTER_ID ||
+ dev->disk->dev->id == GRUB_DISK_DEVICE_CRYPTODISK_ID)
+ return true;
+
+ return false;
+}
+
+static void
+probe_fs_envblk (fs_envblk_spec_t *spec)
+{
+ char **grub_devices = NULL;
+ char **curdev, **curdrive;
+ size_t ndev = 0;
+ char **grub_drives = NULL;
+ grub_device_t grub_dev = NULL;
+ grub_fs_t grub_fs = NULL;
+ bool have_abstraction = false;
+
+ grub_util_biosdisk_init (DEFAULT_DEVICE_MAP);
+ grub_init_all ();
+ grub_gcry_init_all ();
+
+ grub_lvm_fini ();
+ grub_mdraid09_fini ();
+ grub_mdraid1x_fini ();
+ grub_diskfilter_fini ();
+ grub_diskfilter_init ();
+ grub_mdraid09_init ();
+ grub_mdraid1x_init ();
+ grub_lvm_init ();
+
+ grub_devices = grub_guess_root_devices (DEFAULT_DIRECTORY);
+
+ if (grub_devices == NULL || grub_devices[0] == NULL)
+ {
+ grub_util_warn (_("cannot find a device for %s (is /dev mounted?)"), DEFAULT_DIRECTORY);
+ goto cleanup;
+ }
+
+ for (curdev = grub_devices; *curdev != NULL; curdev++, ndev++)
+ grub_util_pull_device (*curdev);
+
+ grub_drives = xcalloc ((ndev + 1), sizeof (grub_drives[0]));
+
+ for (curdev = grub_devices, curdrive = grub_drives; *curdev != NULL; curdev++,
+ curdrive++)
+ {
+ *curdrive = grub_util_get_grub_dev (*curdev);
+ if (*curdrive == NULL)
+ {
+ grub_util_warn (_("cannot find a GRUB drive for %s. Check your device.map"),
+ *curdev);
+ goto cleanup;
+ }
+ }
+ *curdrive = NULL;
+
+ grub_dev = grub_device_open (grub_drives[0]);
+ if (grub_dev == NULL)
+ {
+ grub_util_warn (_("cannot open device %s: %s"), grub_drives[0], grub_errmsg);
+ grub_errno = GRUB_ERR_NONE;
+ goto cleanup;
+ }
+
+ grub_fs = grub_fs_probe (grub_dev);
+ if (grub_fs == NULL)
+ {
+ grub_util_warn (_("cannot probe fs for %s: %s"), grub_drives[0], grub_errmsg);
+ grub_errno = GRUB_ERR_NONE;
+ goto cleanup;
+ }
+
+ have_abstraction = is_abstraction (grub_dev);
+ for (curdrive = grub_drives + 1; *curdrive != NULL && have_abstraction == false; curdrive++)
+ {
+ grub_device_t dev = grub_device_open (*curdrive);
+
+ if (dev == NULL)
+ continue;
+ have_abstraction = is_abstraction (dev);
+ grub_device_close (dev);
+ }
+
+ if (have_abstraction == false)
+ fs_envblk_init (grub_fs->name, grub_devices[0]);
+
+ cleanup:
+ if (grub_devices != NULL)
+ for (curdev = grub_devices; *curdev != NULL; curdev++)
+ free (*curdev);
+ free (grub_devices);
+ free (grub_drives);
+ grub_device_close (grub_dev);
+ grub_gcry_fini_all ();
+ grub_fini_all ();
+ grub_util_biosdisk_fini ();
}
int
@@ -284,6 +680,9 @@ main (int argc, char *argv[])
command = argv[curindex++];
}
+ if (strcmp (filename, DEFAULT_ENVBLK_PATH) == 0)
+ probe_fs_envblk (fs_envblk_spec);
+
if (strcmp (command, "create") == 0)
grub_util_create_envblk_file (filename);
else if (strcmp (command, "list") == 0)
diff --git a/util/grub-install-common.c b/util/grub-install-common.c
index 22bccb6a3..c1c3c5704 100644
--- a/util/grub-install-common.c
+++ b/util/grub-install-common.c
@@ -463,14 +463,19 @@ handle_install_list (struct install_list *il, const char *val,
static char **pubkeys;
static size_t npubkeys;
+static char **x509keys;
+static size_t nx509keys;
static char *sbat;
static int disable_shim_lock;
static grub_compression_t compression;
static int disable_cli;
+static size_t appsig_size;
int
grub_install_parse (int key, char *arg)
{
+ const char *end;
+
switch (key)
{
case GRUB_INSTALL_OPTIONS_INSTALL_CORE_COMPRESS:
@@ -508,6 +513,10 @@ grub_install_parse (int key, char *arg)
case GRUB_INSTALL_OPTIONS_DISABLE_CLI:
disable_cli = 1;
return 1;
+ case 'x':
+ x509keys = xrealloc (x509keys, sizeof (x509keys[0]) * (nx509keys + 1));
+ x509keys[nx509keys++] = xstrdup (arg);
+ return 1;
case GRUB_INSTALL_OPTIONS_VERBOSITY:
verbosity++;
@@ -571,6 +580,13 @@ grub_install_parse (int key, char *arg)
grub_util_error (_("Unrecognized compression `%s'"), arg);
case GRUB_INSTALL_OPTIONS_GRUB_MKIMAGE:
return 1;
+ case GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE:
+ appsig_size = grub_strtoul (arg, &end, 10);
+ if (*arg == '\0' || *end != '\0')
+ grub_util_error (_("non-numeric or invalid appended signature size `%s'"), arg);
+ else if (appsig_size == 0)
+ grub_util_error (_("appended signature size `%s', and it should not be zero"), arg);
+ return 1;
default:
return 0;
}
@@ -632,6 +648,9 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
for (pk = pubkeys; pk < pubkeys + npubkeys; pk++)
slen += sizeof (" --pubkey ''") + grub_strlen (*pk);
+ for (pk = x509keys; pk < x509keys + nx509keys; pk++)
+ slen += sizeof (" --x509key ''") + grub_strlen (*pk);
+
for (md = modules.entries; *md; md++)
slen += sizeof (" ''") + grub_strlen (*md);
@@ -672,6 +691,14 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
*p++ = '\'';
}
+ for (pk = x509keys; pk < x509keys + nx509keys; pk++)
+ {
+ p = grub_stpcpy (p, "--x509key '");
+ p = grub_stpcpy (p, *pk);
+ *p++ = '\'';
+ *p++ = ' ';
+ }
+
for (md = modules.entries; *md; md++)
{
*p++ = ' ';
@@ -683,9 +710,10 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
*p = '\0';
grub_util_info ("grub-mkimage --directory '%s' --prefix '%s' --output '%s'"
- " --format '%s' --compression '%s'%s%s%s%s\n",
+ " --format '%s' --compression '%s'"
+ " --appended-signature-size %zu %s %s %s %s\n",
dir, prefix, outname,
- mkimage_target, compnames[compression],
+ mkimage_target, compnames[compression], appsig_size,
note ? " --note" : "",
disable_shim_lock ? " --disable-shim-lock" : "",
disable_cli ? " --disable-cli" : "", s);
@@ -697,8 +725,8 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
grub_install_generate_image (dir, prefix, fp, outname,
modules.entries, memdisk_path,
- pubkeys, npubkeys, config_path, tgt,
- note, compression, dtb, sbat,
+ pubkeys, npubkeys, x509keys, nx509keys, config_path, tgt,
+ note, appsig_size, compression, dtb, sbat,
disable_shim_lock, disable_cli);
while (dc--)
grub_install_pop_module ();
@@ -775,13 +803,20 @@ copy_all (const char *srcd,
|| strcmp (de->d_name, "..") == 0)
continue;
srcf = grub_util_path_concat (2, srcd, de->d_name);
- if (grub_util_is_special_file (srcf)
- || grub_util_is_directory (srcf))
+ if (grub_util_is_special_file (srcf))
{
free (srcf);
continue;
}
dstf = grub_util_path_concat (2, dstd, de->d_name);
+ if (grub_util_is_directory (srcf))
+ {
+ grub_install_mkdir_p (dstf);
+ copy_all (srcf, dstf);
+ free (srcf);
+ free (dstf);
+ continue;
+ }
grub_install_compress_file (srcf, dstf, 1);
free (srcf);
free (dstf);
diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in
index 32c480dae..45787a1d1 100644
--- a/util/grub-mkconfig.in
+++ b/util/grub-mkconfig.in
@@ -255,7 +255,8 @@ export GRUB_DEFAULT \
GRUB_ENABLE_CRYPTODISK \
GRUB_BADRAM \
GRUB_OS_PROBER_SKIP_LIST \
- GRUB_DISABLE_SUBMENU
+ GRUB_DISABLE_SUBMENU \
+ GRUB_FORCE_EFI_ALL_VIDEO
if test "x${grub_cfg}" != "x"; then
rm -f "${grub_cfg}.new"
diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c
index 547f7310f..fb14aa731 100644
--- a/util/grub-mkimage.c
+++ b/util/grub-mkimage.c
@@ -75,7 +75,8 @@ static struct argp_option options[] = {
/* TRANSLATORS: "embed" is a verb (command description). "*/
{"config", 'c', N_("FILE"), 0, N_("embed FILE as an early config"), 0},
/* TRANSLATORS: "embed" is a verb (command description). "*/
- {"pubkey", 'k', N_("FILE"), 0, N_("embed FILE as public key for signature checking"), 0},
+ {"pubkey", 'k', N_("FILE"), 0, N_("embed FILE as public key for PGP signature checking"), 0},
+ {"x509key", 'x', N_("FILE"), 0, N_("embed FILE as an x509 certificate for appended signature checking"), 0},
/* TRANSLATORS: NOTE is a name of segment. */
{"note", 'n', 0, 0, N_("add NOTE segment for CHRP IEEE1275"), 0},
{"output", 'o', N_("FILE"), 0, N_("output a generated image to FILE [default=stdout]"), 0},
@@ -84,6 +85,7 @@ static struct argp_option options[] = {
{"sbat", 's', N_("FILE"), 0, N_("SBAT metadata"), 0},
{"disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0, N_("disable shim_lock verifier"), 0},
{"disable-cli", GRUB_INSTALL_OPTIONS_DISABLE_CLI, 0, 0, N_("disable command line interface access"), 0},
+ {"appended-signature-size", 'S', N_("SIZE"), 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), 0},
{"verbose", 'v', 0, 0, N_("print verbose messages."), 0},
{ 0, 0, 0, 0, 0, 0 }
};
@@ -124,12 +126,15 @@ struct arguments
char *dtb;
char **pubkeys;
size_t npubkeys;
+ char **x509keys;
+ size_t nx509keys;
char *font;
char *config;
char *sbat;
int note;
int disable_shim_lock;
int disable_cli;
+ size_t appsig_size;
const struct grub_install_image_target_desc *image_target;
grub_compression_t comp;
};
@@ -140,6 +145,7 @@ argp_parser (int key, char *arg, struct argp_state *state)
/* Get the input argument from argp_parse, which we
know is a pointer to our arguments structure. */
struct arguments *arguments = state->input;
+ const char *end;
switch (key)
{
@@ -172,6 +178,14 @@ argp_parser (int key, char *arg, struct argp_state *state)
arguments->note = 1;
break;
+ case 'S':
+ arguments->appsig_size = grub_strtoul (arg, &end, 10);
+ if (*arg == '\0' || *end != '\0')
+ grub_util_error (_("non-numeric or invalid appended signature size `%s'"), arg);
+ else if (arguments->appsig_size == 0)
+ grub_util_error (_("appended signature size `%s', and it should not be zero"), arg);
+ break;
+
case 'm':
if (arguments->memdisk)
free (arguments->memdisk);
@@ -198,6 +212,12 @@ argp_parser (int key, char *arg, struct argp_state *state)
arguments->pubkeys[arguments->npubkeys++] = xstrdup (arg);
break;
+ case 'x':
+ arguments->x509keys = xrealloc (arguments->x509keys,
+ sizeof (arguments->x509keys[0]) * (arguments->nx509keys + 1));
+ arguments->x509keys[arguments->nx509keys++] = xstrdup (arg);
+ break;
+
case 'c':
if (arguments->config)
free (arguments->config);
@@ -328,8 +348,10 @@ main (int argc, char *argv[])
grub_install_generate_image (arguments.dir, arguments.prefix, fp,
arguments.output, arguments.modules,
arguments.memdisk, arguments.pubkeys,
- arguments.npubkeys, arguments.config,
+ arguments.npubkeys, arguments.x509keys,
+ arguments.nx509keys, arguments.config,
arguments.image_target, arguments.note,
+ arguments.appsig_size,
arguments.comp, arguments.dtb,
arguments.sbat, arguments.disable_shim_lock,
arguments.disable_cli);
diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c
index 448862b2e..1a453a5e8 100644
--- a/util/grub-mkimagexx.c
+++ b/util/grub-mkimagexx.c
@@ -115,6 +115,14 @@ struct grub_sbat_note {
char name[ALIGN_UP(sizeof(GRUB_SBAT_NOTE_NAME), 4)];
};
+#define GRUB_APPENDED_SIGNATURE_NOTE_NAME "Appended-Signature"
+#define GRUB_APPENDED_SIGNATURE_NOTE_TYPE 0x41536967 /* "ASig" */
+struct grub_appended_signature_note
+{
+ Elf32_Nhdr header;
+ char name[ALIGN_UP (sizeof (GRUB_APPENDED_SIGNATURE_NOTE_NAME), 4)];
+};
+
static int
is_relocatable (const struct grub_install_image_target_desc *image_target)
{
@@ -216,7 +224,7 @@ grub_arm_reloc_jump24 (grub_uint32_t *target, Elf32_Addr sym_addr)
void
SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc *image_target,
- int note, char *sbat, char **core_img, size_t *core_size,
+ int note, char *sbat, size_t appsig_size, char **core_img, size_t *core_size,
Elf_Addr target_addr,
struct grub_mkimage_layout *layout)
{
@@ -237,6 +245,13 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc
footer_size += ALIGN_UP (sizeof (struct grub_sbat_note) + layout->sbat_size, 4);
}
+ if (appsig_size)
+ {
+ phnum++;
+ footer_size += ALIGN_UP (sizeof (struct grub_appended_signature_note), 4);
+ footer_size += ALIGN_UP_OVERHEAD (appsig_size, 4);
+ }
+
if (image_target->id != IMAGE_LOONGSON_ELF)
phnum += 2;
@@ -518,6 +533,30 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc
memcpy (note_ptr->name, GRUB_SBAT_NOTE_NAME, sizeof (GRUB_SBAT_NOTE_NAME));
memcpy ((char *)(note_ptr + 1), sbat, layout->sbat_size);
+ phdr++;
+ phdr->p_type = grub_host_to_target32 (PT_NOTE);
+ phdr->p_flags = grub_host_to_target32 (PF_R);
+ phdr->p_align = grub_host_to_target32 (image_target->voidp_sizeof);
+ phdr->p_vaddr = 0;
+ phdr->p_paddr = 0;
+ phdr->p_filesz = grub_host_to_target32 (note_size);
+ phdr->p_memsz = 0;
+ phdr->p_offset = grub_host_to_target32 (header_size + program_size + footer_offset);
+ footer += note_size;
+ footer_offset += note_size;
+ }
+
+ if (appsig_size)
+ {
+ int note_size = ALIGN_UP (sizeof (struct grub_appended_signature_note) + appsig_size, 4);
+ struct grub_appended_signature_note *note_ptr = (struct grub_appended_signature_note *) footer;
+
+ note_ptr->header.n_namesz = grub_host_to_target32 (sizeof (GRUB_APPENDED_SIGNATURE_NOTE_NAME));
+ /* Needs to sit at the end, so we round this up and sign some zero padding. */
+ note_ptr->header.n_descsz = grub_host_to_target32 (ALIGN_UP (appsig_size, 4));
+ note_ptr->header.n_type = grub_host_to_target32 (GRUB_APPENDED_SIGNATURE_NOTE_TYPE);
+ strcpy (note_ptr->name, GRUB_APPENDED_SIGNATURE_NOTE_NAME);
+
phdr++;
phdr->p_type = grub_host_to_target32 (PT_NOTE);
phdr->p_flags = grub_host_to_target32 (PF_R);
@@ -1354,7 +1393,7 @@ SUFFIX (relocate_addrs) (Elf_Ehdr *e, struct section_metadata *smd,
*/
sym_addr += addend;
- off = sym_addr - target_section_addr - offset - image_target->vaddr_offset;
+ off = (grub_int64_t) sym_addr - target_section_addr - offset - image_target->vaddr_offset;
switch (ELF_R_TYPE (info))
{
diff --git a/util/grub-mkrescue.c b/util/grub-mkrescue.c
index 6dc71a8a1..e7b9078ab 100644
--- a/util/grub-mkrescue.c
+++ b/util/grub-mkrescue.c
@@ -339,7 +339,7 @@ check_xorriso (const char *val)
const char *argv[5];
int fd;
pid_t pid;
- FILE *mdadm;
+ FILE *fout;
char *buf = NULL;
size_t len = 0;
int ret = 0;
@@ -356,12 +356,12 @@ check_xorriso (const char *val)
if (!pid)
return 0;
- /* Parent. Read mdadm's output. */
- mdadm = fdopen (fd, "r");
- if (! mdadm)
+ /* Parent. Read xorriso's output. */
+ fout = fdopen (fd, "r");
+ if (! fout)
return 0;
- while (getline (&buf, &len, mdadm) > 0)
+ while (getline (&buf, &len, fout) > 0)
{
if (grub_strstr (buf, val))
ret = 1;
@@ -469,7 +469,7 @@ main (int argc, char *argv[])
argp_argv[0] = argv[0];
argp_argc = 1;
- /* argp doesn't allow us to catch unknwon arguments,
+ /* argp doesn't allow us to catch unknown arguments,
so catch them before passing to argp
*/
{
diff --git a/util/grub-pe2elf.c b/util/grub-pe2elf.c
index 11331294f..7a3c5a06e 100644
--- a/util/grub-pe2elf.c
+++ b/util/grub-pe2elf.c
@@ -189,6 +189,7 @@ write_section_data (FILE* fp, const char *name, char *image,
shdr[idx_reloc].sh_name = insert_string (relname);
shdr[idx_reloc].sh_link = i;
+ shdr[idx_reloc].sh_flags = SHF_INFO_LINK;
shdr[idx_reloc].sh_info = idx;
shdr[idx].sh_name = shdr[idx_reloc].sh_name + 4;
@@ -359,7 +360,7 @@ write_symbol_table (FILE* fp, const char *name, char *image,
struct grub_pe32_symbol *pe_symtab;
char *pe_strtab;
Elf_Sym *symtab;
- int *symtab_map, num_syms;
+ int *symtab_map, num_syms, last_stb_local = 0;
int i;
pe_symtab = (struct grub_pe32_symbol *) (image + pe_chdr->symtab_offset);
@@ -390,7 +391,10 @@ write_symbol_table (FILE* fp, const char *name, char *image,
if (pe_symtab->storage_class == GRUB_PE32_SYM_CLASS_EXTERNAL)
bind = STB_GLOBAL;
else
- bind = STB_LOCAL;
+ {
+ bind = STB_LOCAL;
+ last_stb_local = num_syms;
+ }
if ((pe_symtab->type != GRUB_PE32_DT_FUNCTION) && (pe_symtab->num_aux))
{
@@ -440,6 +444,7 @@ write_symbol_table (FILE* fp, const char *name, char *image,
shdr[symtab_section].sh_size = num_syms * sizeof (Elf_Sym);
shdr[symtab_section].sh_entsize = sizeof (Elf_Sym);
shdr[symtab_section].sh_link = strtab_section;
+ shdr[symtab_section].sh_info = ++last_stb_local;
shdr[symtab_section].sh_addralign = 4;
grub_util_write_image_at (symtab, shdr[symtab_section].sh_size,
diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in
index f86b69bad..77834cfaf 100644
--- a/util/grub.d/00_header.in
+++ b/util/grub.d/00_header.in
@@ -46,6 +46,13 @@ cat << EOF
if [ -s \$prefix/grubenv ]; then
load_env
fi
+
+if [ "\${env_block}" ] ; then
+ set env_block="(\${root})\${env_block}"
+ export env_block
+ load_env -f "\${env_block}"
+fi
+
EOF
if [ "x$GRUB_BUTTON_CMOS_ADDRESS" != "x" ]; then
cat </dev/null || true`
+ rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || zdb -l ${GRUB_DEVICE} | awk -F \' '/ name/ { print $2 }'`
bootfs="`make_system_path_relative_to_its_root / | sed -e "s,@$,,"`"
LINUX_ROOT_DEVICE="ZFS=${rpool}${bootfs%/}"
;;
diff --git a/util/grub.d/20_linux_xen.in b/util/grub.d/20_linux_xen.in
index 94dd8be13..27bff00a8 100644
--- a/util/grub.d/20_linux_xen.in
+++ b/util/grub.d/20_linux_xen.in
@@ -81,7 +81,7 @@ case x"$GRUB_FS" in
GRUB_CMDLINE_LINUX="rootflags=subvol=${rootsubvol} ${GRUB_CMDLINE_LINUX}"
fi;;
xzfs)
- rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || true`
+ rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || zdb -l ${GRUB_DEVICE} | awk -F \' '/ name/ { print $2 }'`
bootfs="`make_system_path_relative_to_its_root / | sed -e "s,@$,,"`"
LINUX_ROOT_DEVICE="ZFS=${rpool}${bootfs%/}"
;;
diff --git a/util/import_gcry.py b/util/import_gcry.py
index 086bde77c..b2131643b 100644
--- a/util/import_gcry.py
+++ b/util/import_gcry.py
@@ -145,8 +145,8 @@ with codecs.open (os.path.join (cipher_dir_out, "crypto.lst"), "w", "utf-8") as
if re.match (r"(Makefile\.am|primegen\.c|cipher\.c|cipher-.*\.c|mac-.*\.c|mac\.c|pubkey\.c)$", cipher_file):
chlog = "%s%s: Removed\n" % (chlog, chlognew)
continue
- # TODO: Support KDF
- if re.match (r"(kdf\.c|scrypt\.c)$", cipher_file):
+ # TODO: Support scrypt KDF
+ if re.match (r"(scrypt\.c)$", cipher_file):
chlog = "%s%s: Removed\n" % (chlog, chlognew)
continue
# TODO: Support chacha20 and poly1305
@@ -313,6 +313,17 @@ with codecs.open (os.path.join (cipher_dir_out, "crypto.lst"), "w", "utf-8") as
chlognew = "%s %s" % (chlognew, chmsg)
nch = True
continue
+ elif re.match ("_gcry_kdf_selftest|check_one|_gcry_kdf_pkdf2|_gcry_kdf_derive|openpgp_s2k|ballon_context_size|balloon_*|prng_aes_*|onestep_kdf_*|hkdf_*|x963_kdf_*", line) is not None and cipher_file == "kdf.c":
+ # TODO Support other KDFs
+ skip = 1
+ fname = re.match ("[a-zA-Z0-9_]*", line).group ()
+ chmsg = "(%s): Removed." % fname
+ if nch:
+ chlognew = "%s\n %s" % (chlognew, chmsg)
+ else:
+ chlognew = "%s %s" % (chlognew, chmsg)
+ nch = True
+ continue
else:
fw.write (holdline)
m = re.match ("# *include <(.*)>", line)
@@ -439,7 +450,7 @@ with codecs.open (os.path.join (cipher_dir_out, "crypto.lst"), "w", "utf-8") as
nch = True
continue
- m = re.match (r"((static )?const char( |)\*|static const gcry_md_spec_t \*|(static )?gpg_err_code_t|gpg_error_t|void|(static )?int|(static )?unsigned int|(static )?gcry_err_code_t|static gcry_mpi_t|static void|void|static elliptic_curve_t) *$", line)
+ m = re.match (r"((static )?const char( |)\*|static const gcry_md_spec_t \*|(static )?gpg_err_code_t|gpg_error_t|void|(static )?int|(static )?unsigned int|(static )?gcry_err_code_t|static gcry_mpi_t|static void|void|static elliptic_curve_t|static u64|static size_t) *$", line)
if not m is None:
hold = True
holdline = line
@@ -575,6 +586,20 @@ with codecs.open (os.path.join (cipher_dir_out, "crypto.lst"), "w", "utf-8") as
conf.write ("};\n\n")
if nch:
chlog = "%s%s\n" % (chlog, chlognew)
+ elif cipher_file == "kdf.c":
+ modfiles = ["kdf.c"]
+ if modname in extra_files:
+ modfiles += extra_files[modname]
+ conf.write ("module = {\n")
+ conf.write (" name = %s;\n" % modname)
+ for src in modfiles:
+ conf.write (" common = lib/libgcrypt-grub/cipher/%s;\n" % src)
+ confutil.write (" common = grub-core/lib/libgcrypt-grub/cipher/%s;\n" % src)
+ conf.write (" cflags = '$(CFLAGS_GCRY)';\n")
+ conf.write (" cppflags = '$(CPPFLAGS_GCRY)';\n")
+ conf.write ("};\n\n")
+ if nch:
+ chlog = "%s%s\n" % (chlog, chlognew)
elif isc and cipher_file not in extra_files_list:
print ("WARNING: C file isn't a module: %s" % cipher_file)
os.remove (outfile)
@@ -608,7 +633,11 @@ with codecs.open (os.path.join (cipher_dir_out, "crypto.lst"), "w", "utf-8") as
continue
with codecs.open (infile, "r", "utf-8") as f:
if src == "types.h":
- fw.write (f.read ().replace ("float f;", "").replace ("double g;", ""))
+ fw.write (f.read ().replace ("float f;", "").replace ("double g;", "") \
+ .replace("#ifndef HAVE_BYTE",
+ "#ifdef __clang__\n" \
+ "#pragma GCC diagnostic ignored \"-Wtypedef-redefinition\"\n#endif\n" \
+ "#ifndef HAVE_BYTE"))
continue
if src == "cipher-proto.h":
diff --git a/util/misc.c b/util/misc.c
index 0f928e5b4..6e16a68d9 100644
--- a/util/misc.c
+++ b/util/misc.c
@@ -101,7 +101,7 @@ grub_util_read_image (const char *path)
}
void
-grub_util_write_image_at (const void *img, size_t size, off_t offset, FILE *out,
+grub_util_write_image_at (const void *img, size_t size, grub_off_t offset, FILE *out,
const char *name)
{
grub_util_info ("writing 0x%" GRUB_HOST_PRIxLONG_LONG " bytes at offset 0x%"
diff --git a/util/mkimage.c b/util/mkimage.c
index b46df2909..2920c0249 100644
--- a/util/mkimage.c
+++ b/util/mkimage.c
@@ -56,6 +56,9 @@
#pragma GCC diagnostic ignored "-Wcast-align"
+#define SBAT_HEADER "sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md"
+#define SBAT_HEADER_SIZE (sizeof (SBAT_HEADER))
+
#define TARGET_NO_FIELD 0xffffffff
/* use 2015-01-01T00:00:00+0000 as a stock timestamp */
@@ -883,9 +886,9 @@ void
grub_install_generate_image (const char *dir, const char *prefix,
FILE *out, const char *outname, char *mods[],
char *memdisk_path, char **pubkey_paths,
- size_t npubkeys, char *config_path,
+ size_t npubkeys, char **x509key_paths, size_t nx509keys, char *config_path,
const struct grub_install_image_target_desc *image_target,
- int note, grub_compression_t comp, const char *dtb_path,
+ int note, size_t appsig_size, grub_compression_t comp, const char *dtb_path,
const char *sbat_path, int disable_shim_lock,
int disable_cli)
{
@@ -929,6 +932,24 @@ grub_install_generate_image (const char *dir, const char *prefix,
}
}
+ if (nx509keys != 0 && image_target->id != IMAGE_PPC)
+ grub_util_error (_("x509 public key can be support only to appended signature"
+ " with powerpc-ieee1275 images"));
+
+ {
+ size_t i;
+
+ for (i = 0; i < nx509keys; i++)
+ {
+ size_t curs;
+
+ curs = ALIGN_ADDR (grub_util_get_image_size (x509key_paths[i]));
+ grub_util_info ("the size of x509 public key %u is 0x%" GRUB_HOST_PRIxLONG_LONG,
+ (unsigned) i, (unsigned long long) curs);
+ total_module_size += curs + sizeof (struct grub_module_header);
+ }
+ }
+
if (memdisk_path)
{
memdisk_size = ALIGN_UP(grub_util_get_image_size (memdisk_path), 512);
@@ -945,6 +966,15 @@ grub_install_generate_image (const char *dir, const char *prefix,
if (sbat_path != NULL && (image_target->id != IMAGE_EFI && image_target->id != IMAGE_PPC))
grub_util_error (_("SBAT data can be added only to EFI or powerpc-ieee1275 images"));
+ else if (sbat_path != NULL)
+ {
+ sbat_size = grub_util_get_image_size (sbat_path);
+ if (sbat_size < SBAT_HEADER_SIZE)
+ grub_util_error (_("%s file should contain at least an SBAT header"), sbat_path);
+ }
+
+ if (appsig_size != 0 && image_target->id != IMAGE_PPC)
+ grub_util_error (_("appended signature can be support only to powerpc-ieee1275 images"));
if (disable_shim_lock)
total_module_size += sizeof (struct grub_module_header);
@@ -1053,7 +1083,7 @@ grub_install_generate_image (const char *dir, const char *prefix,
curs = grub_util_get_image_size (pubkey_paths[i]);
header = (struct grub_module_header *) (kernel_img + offset);
- header->type = grub_host_to_target32 (OBJ_TYPE_PUBKEY);
+ header->type = grub_host_to_target32 (OBJ_TYPE_GPG_PUBKEY);
header->size = grub_host_to_target32 (curs + sizeof (*header));
offset += sizeof (*header);
@@ -1062,6 +1092,25 @@ grub_install_generate_image (const char *dir, const char *prefix,
}
}
+ {
+ size_t i;
+
+ for (i = 0; i < nx509keys; i++)
+ {
+ size_t curs;
+ struct grub_module_header *header;
+
+ curs = grub_util_get_image_size (x509key_paths[i]);
+ header = (struct grub_module_header *) (kernel_img + offset);
+ header->type = grub_host_to_target32 (OBJ_TYPE_X509_PUBKEY);
+ header->size = grub_host_to_target32 (curs + sizeof (*header));
+
+ offset += sizeof (*header);
+ grub_util_load_image (x509key_paths[i], kernel_img + offset);
+ offset += ALIGN_ADDR (curs);
+ }
+ }
+
if (memdisk_path)
{
struct grub_module_header *header;
@@ -1356,7 +1405,7 @@ grub_install_generate_image (const char *dir, const char *prefix,
if (sbat_path != NULL)
{
- sbat_size = ALIGN_ADDR (grub_util_get_image_size (sbat_path));
+ sbat_size = ALIGN_ADDR (sbat_size);
sbat_size = ALIGN_UP (sbat_size, GRUB_PE32_FILE_ALIGNMENT);
}
@@ -1817,7 +1866,6 @@ grub_install_generate_image (const char *dir, const char *prefix,
char *sbat = NULL;
if (sbat_path != NULL)
{
- sbat_size = grub_util_get_image_size (sbat_path);
sbat = xmalloc (sbat_size);
grub_util_load_image (sbat_path, sbat);
layout.sbat_size = sbat_size;
@@ -1833,10 +1881,10 @@ grub_install_generate_image (const char *dir, const char *prefix,
else
target_addr = image_target->link_addr;
if (image_target->voidp_sizeof == 4)
- grub_mkimage_generate_elf32 (image_target, note, sbat, &core_img, &core_size,
+ grub_mkimage_generate_elf32 (image_target, note, sbat, appsig_size, &core_img, &core_size,
target_addr, &layout);
else
- grub_mkimage_generate_elf64 (image_target, note, sbat, &core_img, &core_size,
+ grub_mkimage_generate_elf64 (image_target, note, sbat, appsig_size, &core_img, &core_size,
target_addr, &layout);
}
break;