powerpc: Add appended signature feature

Resolves: #RHEL-24510
Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
This commit is contained in:
Nicolas Frayer 2025-11-19 14:41:20 +01:00
parent f4f7a97672
commit 17ffd9b3e0
39 changed files with 275880 additions and 3 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,299 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Vladimir Serbinenko <phcoder@gmail.com>
Date: Mon, 7 Jul 2025 14:52:08 +0000
Subject: [PATCH] b64dec: Import b64dec from gpg-error
Imported from libgpg-error 1.51.
Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/lib/b64dec.c | 279 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 279 insertions(+)
create mode 100644 grub-core/lib/b64dec.c
diff --git a/grub-core/lib/b64dec.c b/grub-core/lib/b64dec.c
new file mode 100644
index 0000000..868d985
--- /dev/null
+++ b/grub-core/lib/b64dec.c
@@ -0,0 +1,279 @@
+/* b64dec.c - Simple Base64 decoder.
+ * Copyright (C) 2008, 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2008, 2011, 2016 g10 Code GmbH
+ *
+ * This file is part of Libgpg-error.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This file 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This file was originally a part of GnuPG.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gpgrt-int.h"
+
+
+/* The reverse base-64 list used for base-64 decoding. */
+static unsigned char const asctobin[128] =
+ {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+ 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+ 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+ 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+
+enum decoder_states
+ {
+ s_init, s_idle, s_lfseen, s_beginseen, s_waitheader, s_waitblank, s_begin,
+ s_b64_0, s_b64_1, s_b64_2, s_b64_3,
+ s_waitendtitle, s_waitend
+ };
+
+
+
+/* Allocate and initialize the context for the base64 decoder. If
+ TITLE is NULL a plain base64 decoding is done. If it is the empty
+ string the decoder will skip everything until a "-----BEGIN " line
+ has been seen, decoding ends at a "----END " line. */
+gpgrt_b64state_t
+_gpgrt_b64dec_start (const char *title)
+{
+ gpgrt_b64state_t state;
+ char *t = NULL;
+
+ if (title)
+ {
+ t = xtrystrdup (title);
+ if (!t)
+ return NULL;
+ }
+
+ state = xtrycalloc (1, sizeof (struct _gpgrt_b64state));
+ if (!state)
+ {
+ xfree (t);
+ return NULL;
+ }
+
+ if (t)
+ {
+ state->title = t;
+ state->idx = s_init;
+ }
+ else
+ state->idx = s_b64_0;
+
+ state->using_decoder = 1;
+
+ return state;
+}
+
+
+/* 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)
+{
+ enum decoder_states ds = state->idx;
+ unsigned char val = state->radbuf[0];
+ int pos = state->quad_count;
+ char *d, *s;
+
+ if (state->lasterr)
+ return state->lasterr;
+
+ if (state->stop_seen)
+ {
+ *r_nbytes = 0;
+ state->lasterr = GPG_ERR_EOF;
+ xfree (state->title);
+ state->title = NULL;
+ return state->lasterr;
+ }
+
+ for (s=d=buffer; length && !state->stop_seen; length--, s++)
+ {
+ again:
+ switch (ds)
+ {
+ case s_idle:
+ if (*s == '\n')
+ {
+ ds = s_lfseen;
+ pos = 0;
+ }
+ break;
+ case s_init:
+ ds = s_lfseen;
+ /* Fall through */
+ case s_lfseen:
+ if (*s != "-----BEGIN "[pos])
+ {
+ ds = s_idle;
+ goto again;
+ }
+ else if (pos == 10)
+ {
+ pos = 0;
+ ds = s_beginseen;
+ }
+ else
+ pos++;
+ break;
+ case s_beginseen:
+ if (*s != "PGP "[pos])
+ ds = s_begin; /* Not a PGP armor. */
+ else if (pos == 3)
+ ds = s_waitheader;
+ else
+ pos++;
+ break;
+ case s_waitheader:
+ if (*s == '\n')
+ ds = s_waitblank;
+ break;
+ case s_waitblank:
+ if (*s == '\n')
+ ds = s_b64_0; /* blank line found. */
+ else if (*s == ' ' || *s == '\r' || *s == '\t')
+ ; /* Ignore spaces. */
+ else
+ {
+ /* Armor header line. Note that we don't care that our
+ * FSM accepts a header prefixed with spaces. */
+ ds = s_waitheader; /* Wait for next header. */
+ }
+ break;
+ case s_begin:
+ if (*s == '\n')
+ ds = s_b64_0;
+ break;
+ case s_b64_0:
+ case s_b64_1:
+ case s_b64_2:
+ case s_b64_3:
+ {
+ int c;
+
+ if (*s == '-' && state->title)
+ {
+ /* Not a valid Base64 character: assume end
+ header. */
+ ds = s_waitend;
+ }
+ else if (*s == '=')
+ {
+ /* Pad character: stop */
+ if (ds == s_b64_1)
+ *d++ = val;
+ ds = state->title? s_waitendtitle : s_waitend;
+ }
+ else if (*s == '\n' || *s == ' ' || *s == '\r' || *s == '\t')
+ ; /* Skip white spaces. */
+ else if ( (*s & 0x80)
+ || (c = asctobin[*(unsigned char *)s]) == 255)
+ {
+ /* Skip invalid encodings. */
+ state->invalid_encoding = 1;
+ }
+ else if (ds == s_b64_0)
+ {
+ val = c << 2;
+ ds = s_b64_1;
+ }
+ else if (ds == s_b64_1)
+ {
+ val |= (c>>4)&3;
+ *d++ = val;
+ val = (c<<4)&0xf0;
+ ds = s_b64_2;
+ }
+ else if (ds == s_b64_2)
+ {
+ val |= (c>>2)&15;
+ *d++ = val;
+ val = (c<<6)&0xc0;
+ ds = s_b64_3;
+ }
+ else
+ {
+ val |= c&0x3f;
+ *d++ = val;
+ ds = s_b64_0;
+ }
+ }
+ break;
+ case s_waitendtitle:
+ if (*s == '-')
+ ds = s_waitend;
+ break;
+ case s_waitend:
+ if ( *s == '\n')
+ state->stop_seen = 1;
+ break;
+ default:
+ gpgrt_assert (!"invalid state");
+ }
+ }
+
+
+ state->idx = ds;
+ state->radbuf[0] = val;
+ state->quad_count = pos;
+ *r_nbytes = (d -(char*) buffer);
+ return 0;
+}
+
+
+/* Return an error code in case an encoding error has been found
+ during decoding. */
+gpg_err_code_t
+_gpgrt_b64dec_finish (gpgrt_b64state_t state)
+{
+ gpg_error_t err;
+
+ if (!state)
+ return 0; /* Already released. */
+
+ if (!state->using_decoder)
+ err = GPG_ERR_CONFLICT; /* State was allocated for the encoder. */
+ else if (state->lasterr)
+ err = state->lasterr;
+ else
+ {
+ xfree (state->title);
+ err = state->invalid_encoding? GPG_ERR_BAD_DATA : 0;
+ }
+ xfree (state);
+
+ return err;
+}

View File

@ -0,0 +1,42 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Vladimir Serbinenko <phcoder@gmail.com>
Date: Mon, 7 Jul 2025 14:52:09 +0000
Subject: [PATCH] b64dec: Add harness for compilation in GRUB environment
Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/lib/gpgrt-int.h | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
create mode 100644 grub-core/lib/gpgrt-int.h
diff --git a/grub-core/lib/gpgrt-int.h b/grub-core/lib/gpgrt-int.h
new file mode 100644
index 0000000..45d6358
--- /dev/null
+++ b/grub-core/lib/gpgrt-int.h
@@ -0,0 +1,24 @@
+#include <grub/crypto.h>
+
+struct _gpgrt_b64state
+{
+ int idx;
+ int quad_count;
+ char *title;
+ unsigned char radbuf[4];
+ unsigned int crc;
+ gpg_err_code_t lasterr;
+ unsigned int flags;
+ unsigned int stop_seen:1;
+ unsigned int invalid_encoding:1;
+ unsigned int using_decoder:1;
+};
+
+#define _gpgrt_b64dec_start gpgrt_b64dec_start
+#define xtrystrdup grub_strdup
+#define xtrycalloc grub_calloc
+#define xfree grub_free
+#define _gpgrt_b64dec_finish gpgrt_b64dec_finish
+#define gpgrt_assert(expr) ((expr)? (void)0 \
+ : _gcry_assert_failed (#expr, __FILE__, __LINE__, __FUNCTION__))
+#define _gpgrt_b64dec_proc gpgrt_b64dec_proc

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,285 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Vladimir Serbinenko <phcoder@gmail.com>
Date: Mon, 7 Jul 2025 14:52:11 +0000
Subject: [PATCH] tests: Add DSA and RSA SEXP tests
This allows us to test purely the integration of the implementation of
DSA and RSA from libgcrypt without concerning with additional code.
Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/Makefile.core.def | 16 +++++
grub-core/tests/dsa_sexp_test.c | 127 ++++++++++++++++++++++++++++++++++++++++
grub-core/tests/rsa_sexp_test.c | 101 ++++++++++++++++++++++++++++++++
3 files changed, 244 insertions(+)
create mode 100644 grub-core/tests/dsa_sexp_test.c
create mode 100644 grub-core/tests/rsa_sexp_test.c
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 4ba5d94..0b6b996 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2215,6 +2215,22 @@ module = {
common = tests/setjmp_test.c;
};
+module = {
+ name = dsa_sexp_test;
+ common = tests/dsa_sexp_test.c;
+
+ cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls -Wno-sign-compare';
+ cppflags = '$(CPPFLAGS_GCRY)';
+};
+
+module = {
+ name = rsa_sexp_test;
+ common = tests/rsa_sexp_test.c;
+
+ cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls -Wno-sign-compare';
+ cppflags = '$(CPPFLAGS_GCRY)';
+};
+
module = {
name = appended_signature_test;
common = tests/appended_signature_test.c;
diff --git a/grub-core/tests/dsa_sexp_test.c b/grub-core/tests/dsa_sexp_test.c
new file mode 100644
index 0000000..31600d9
--- /dev/null
+++ b/grub-core/tests/dsa_sexp_test.c
@@ -0,0 +1,127 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/test.h>
+#include <grub/crypto.h>
+#include <grub/gcrypt/gcrypt.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+/* Sample DSA public key. */
+static char pubkey_dump[] = {
+ 0x28, 0x31, 0x30, 0x3a, 0x70, 0x75, 0x62, 0x6c,
+ 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x28, 0x33,
+ 0x3a, 0x64, 0x73, 0x61, 0x28, 0x31, 0x3a, 0x70,
+ 0x31, 0x32, 0x39, 0x3a, 0x00, 0xc0, 0x50, 0x14,
+ 0x4c, 0x97, 0x10, 0x69, 0x07, 0xa7, 0xe9, 0x2b,
+ 0xe5, 0xc6, 0x88, 0xe1, 0x6d, 0xd8, 0x38, 0x28,
+ 0x09, 0x49, 0x5b, 0xe8, 0xa3, 0x04, 0xb8, 0xc4,
+ 0x6e, 0x98, 0xc1, 0xc2, 0xb0, 0x2a, 0xe0, 0xe2,
+ 0x1a, 0x30, 0xd2, 0xdb, 0x45, 0x1a, 0x88, 0x80,
+ 0x28, 0x24, 0xb0, 0xbf, 0xc2, 0xbd, 0xe9, 0xf6,
+ 0x9d, 0xa2, 0x01, 0x94, 0xe6, 0x7f, 0xa0, 0xb6,
+ 0xe4, 0x39, 0xfc, 0x54, 0xba, 0x99, 0xb6, 0xbe,
+ 0x39, 0xee, 0xa5, 0xd9, 0xa0, 0x35, 0x3c, 0x2d,
+ 0x3e, 0x96, 0xc3, 0x96, 0xa5, 0x0d, 0x2b, 0xbf,
+ 0x3b, 0xa3, 0xe2, 0xe8, 0x89, 0xed, 0x60, 0xe0,
+ 0x43, 0x61, 0xb6, 0x73, 0xf6, 0xa7, 0xb4, 0x56,
+ 0x76, 0x04, 0xf7, 0x8b, 0xf1, 0x84, 0xaa, 0x3e,
+ 0xe0, 0x08, 0xad, 0xdd, 0xc2, 0x36, 0xfd, 0x3d,
+ 0xd0, 0xad, 0xf4, 0x3a, 0x7e, 0x80, 0x8c, 0x52,
+ 0x2b, 0x04, 0xa8, 0x03, 0x27, 0x29, 0x28, 0x31,
+ 0x3a, 0x71, 0x32, 0x31, 0x3a, 0x00, 0xd5, 0x34,
+ 0xd2, 0xc5, 0x1c, 0x26, 0xdf, 0xb0, 0xba, 0x78,
+ 0x75, 0xe5, 0xe9, 0x36, 0x6b, 0x04, 0x03, 0xe2,
+ 0x57, 0x3f, 0x29, 0x28, 0x31, 0x3a, 0x67, 0x31,
+ 0x32, 0x38, 0x3a, 0x3b, 0xa0, 0xac, 0xa3, 0xa1,
+ 0xd1, 0x04, 0x23, 0x5f, 0x9f, 0xbc, 0x6d, 0x9e,
+ 0x88, 0x2a, 0x28, 0xc1, 0x48, 0xaf, 0xa5, 0x17,
+ 0x59, 0x3a, 0x17, 0x33, 0x56, 0xaa, 0x8d, 0x27,
+ 0x64, 0xfe, 0x8e, 0x8a, 0x2e, 0xba, 0xf2, 0x66,
+ 0xcc, 0x66, 0xbd, 0xa4, 0xfe, 0xa9, 0x07, 0x0d,
+ 0xae, 0x8c, 0x9f, 0x70, 0xf7, 0x87, 0xaa, 0x01,
+ 0x47, 0x6b, 0xf9, 0x0f, 0x09, 0x18, 0x42, 0x76,
+ 0xc4, 0xa3, 0xb9, 0x55, 0x11, 0x8d, 0xa3, 0xa5,
+ 0x69, 0x30, 0x91, 0xb7, 0x03, 0xef, 0x7f, 0x12,
+ 0xe6, 0xb9, 0x78, 0x73, 0xe0, 0xc0, 0x4f, 0xc6,
+ 0xd9, 0x43, 0x99, 0x95, 0x0b, 0x4d, 0x58, 0xd3,
+ 0x6b, 0x76, 0xb0, 0x6a, 0xcf, 0x68, 0x6d, 0xf0,
+ 0xd9, 0xc1, 0x88, 0x43, 0x9d, 0xf9, 0x04, 0xcb,
+ 0xc9, 0x82, 0x6c, 0xee, 0xd4, 0x9c, 0xbd, 0x1c,
+ 0x4d, 0x54, 0x29, 0x83, 0xa9, 0x5e, 0xaa, 0x10,
+ 0xa7, 0xc1, 0x04, 0x29, 0x28, 0x31, 0x3a, 0x79,
+ 0x31, 0x32, 0x39, 0x3a, 0x00, 0x82, 0x33, 0xf1,
+ 0x91, 0xe3, 0xf2, 0x12, 0x93, 0x5a, 0xed, 0x0c,
+ 0x9d, 0xec, 0x67, 0xaa, 0xa7, 0x97, 0x7f, 0x9f,
+ 0x5e, 0xef, 0x6a, 0x3e, 0xa4, 0x7f, 0x9b, 0xed,
+ 0x65, 0xd7, 0xba, 0x40, 0x6d, 0xe1, 0xde, 0xc1,
+ 0x14, 0x4c, 0x9b, 0x28, 0x5c, 0x03, 0x8e, 0x1a,
+ 0xd4, 0x1b, 0x80, 0x1b, 0x07, 0xd6, 0x84, 0x04,
+ 0x49, 0x6c, 0x1b, 0x08, 0x84, 0x15, 0x54, 0x62,
+ 0xca, 0xd5, 0x75, 0xff, 0xc8, 0xb3, 0x81, 0x76,
+ 0x82, 0x91, 0x35, 0x80, 0x20, 0x73, 0x2a, 0x21,
+ 0xca, 0x22, 0x06, 0xa7, 0x73, 0x99, 0x75, 0x7e,
+ 0x5e, 0xa6, 0x09, 0x59, 0x66, 0x2c, 0xcd, 0xb1,
+ 0x8d, 0x3b, 0xc0, 0x68, 0xc5, 0x41, 0xa0, 0x9d,
+ 0x82, 0x15, 0xc4, 0xdd, 0x47, 0x1c, 0x5b, 0xa9,
+ 0x74, 0x18, 0xaf, 0x72, 0x63, 0x6b, 0x0a, 0x4e,
+ 0x95, 0x09, 0x7a, 0xb5, 0x4b, 0x98, 0x85, 0xb9,
+ 0x6d, 0x9d, 0x3b, 0x73, 0x8c, 0x29, 0x29, 0x29,
+};
+
+/* Sample DSA signature of message "hello" with sample key. */
+static char sig_dump[] = {
+ 0x28, 0x37, 0x3a, 0x73, 0x69, 0x67, 0x2d, 0x76,
+ 0x61, 0x6c, 0x28, 0x33, 0x3a, 0x64, 0x73, 0x61,
+ 0x28, 0x31, 0x3a, 0x72, 0x32, 0x30, 0x3a, 0xb6,
+ 0x60, 0x37, 0xef, 0x02, 0x7c, 0x7c, 0x6e, 0x4f,
+ 0x66, 0x8c, 0x7c, 0x26, 0x77, 0xd9, 0x33, 0x90,
+ 0xba, 0x7c, 0xfb, 0x29, 0x28, 0x31, 0x3a, 0x73,
+ 0x32, 0x30, 0x3a, 0x83, 0xc0, 0x84, 0x72, 0xc6,
+ 0x1c, 0x85, 0x6f, 0x8b, 0x9b, 0xb0, 0x38, 0x38,
+ 0xb2, 0xb6, 0xdf, 0x1c, 0x52, 0x96, 0x1b, 0x29,
+ 0x29, 0x29,
+};
+
+extern gcry_pk_spec_t _gcry_pubkey_spec_dsa;
+
+static void
+dsa_sexp_test (void)
+{
+ gcry_sexp_t sign_parms, sign_parms_invalid, pubkey, sig;
+ int rc;
+ grub_size_t errof;
+
+ rc = _gcry_sexp_build (&sign_parms_invalid, &errof,
+ "(data (value \"hi\"))\n");
+ grub_test_assert (rc == 0, "sexp build failed");
+
+ rc = _gcry_sexp_build (&sign_parms, &errof,
+ "(data (value \"hello\"))\n");
+ grub_test_assert (rc == 0, "sexp build failed");
+ rc = _gcry_sexp_new (&pubkey, pubkey_dump, sizeof(pubkey_dump), 0);
+ grub_test_assert (rc == 0, "sexp new failed");
+ rc = _gcry_sexp_new (&sig, sig_dump, sizeof(sig_dump), 0);
+ grub_test_assert (rc == 0, "sexp new failed");
+ rc = _gcry_pubkey_spec_dsa.verify (sig, sign_parms, pubkey);
+ grub_test_assert (rc == 0, "signature verification failed: %d", rc);
+ rc = _gcry_pubkey_spec_dsa.verify (sig, sign_parms_invalid, pubkey);
+ grub_test_assert (rc != 0, "signature verification succeded wrongly");
+}
+
+GRUB_FUNCTIONAL_TEST (dsa_sexp_test, dsa_sexp_test);
diff --git a/grub-core/tests/rsa_sexp_test.c b/grub-core/tests/rsa_sexp_test.c
new file mode 100644
index 0000000..6ebba81
--- /dev/null
+++ b/grub-core/tests/rsa_sexp_test.c
@@ -0,0 +1,101 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/test.h>
+#include <grub/crypto.h>
+#include <grub/gcrypt/gcrypt.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+/* Sample RSA key. */
+static char pubkey_dump[] = {
+ 0x28, 0x31, 0x30, 0x3a, 0x70, 0x75, 0x62, 0x6c,
+ 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x28, 0x33,
+ 0x3a, 0x72, 0x73, 0x61, 0x28, 0x31, 0x3a, 0x6e,
+ 0x31, 0x32, 0x39, 0x3a, 0x00, 0xe1, 0x35, 0xc1,
+ 0x97, 0x90, 0xe8, 0x54, 0xa8, 0x3b, 0x97, 0x05,
+ 0xaf, 0x45, 0xaf, 0x67, 0xbf, 0xec, 0x07, 0xbe,
+ 0x9b, 0x55, 0x9c, 0x3f, 0x47, 0xae, 0x25, 0xb6,
+ 0xe3, 0x23, 0x99, 0x10, 0x5e, 0x17, 0x1a, 0xda,
+ 0x33, 0xe6, 0x73, 0x0d, 0x96, 0x9c, 0x5c, 0x25,
+ 0x13, 0x5d, 0x49, 0xb9, 0x86, 0xc0, 0xb1, 0x80,
+ 0x29, 0x20, 0xb2, 0x91, 0x72, 0x43, 0xcc, 0x2a,
+ 0x67, 0xd3, 0x11, 0xe8, 0x7b, 0x21, 0x75, 0xf6,
+ 0x1b, 0xc0, 0xb2, 0x01, 0xc8, 0x35, 0xaa, 0xfb,
+ 0xa7, 0x29, 0xb4, 0xb9, 0x94, 0x4e, 0x53, 0x49,
+ 0x82, 0x74, 0xe9, 0x23, 0x69, 0xa4, 0xf6, 0xdf,
+ 0x40, 0x2a, 0x73, 0x01, 0xfa, 0xe7, 0xf8, 0x32,
+ 0x6b, 0x57, 0xfe, 0xb9, 0x7d, 0x02, 0xc2, 0xfb,
+ 0x7f, 0x99, 0x1f, 0x6e, 0x8d, 0x53, 0x01, 0x56,
+ 0xaf, 0x46, 0x62, 0xb3, 0xe0, 0xa8, 0xa6, 0x0a,
+ 0x55, 0x2c, 0x4b, 0x85, 0x5b, 0x29, 0x28, 0x31,
+ 0x3a, 0x65, 0x33, 0x3a, 0x01, 0x00, 0x01, 0x29,
+ 0x29, 0x29,
+};
+
+/* Sample RSA signature of message "hello" with sample key. */
+static char sig_dump[] = {
+ 0x28, 0x37, 0x3a, 0x73, 0x69, 0x67, 0x2d, 0x76,
+ 0x61, 0x6c, 0x28, 0x33, 0x3a, 0x72, 0x73, 0x61,
+ 0x28, 0x31, 0x3a, 0x73, 0x31, 0x32, 0x38, 0x3a,
+ 0x5b, 0x2d, 0xeb, 0xa5, 0x4b, 0x8b, 0xd9, 0x92,
+ 0x66, 0x57, 0x89, 0xd8, 0x31, 0xc0, 0x0e, 0x53,
+ 0xf8, 0x1c, 0x4f, 0xc8, 0x79, 0x67, 0xb9, 0x10,
+ 0xe5, 0x63, 0x5f, 0xef, 0xb1, 0x0b, 0x0e, 0x7f,
+ 0xed, 0x86, 0x06, 0xa8, 0x05, 0xbf, 0x6b, 0xd1,
+ 0x36, 0x41, 0x08, 0x3b, 0xd0, 0xbd, 0xef, 0xb7,
+ 0xc2, 0x69, 0xb8, 0xb4, 0x3e, 0x2c, 0xb5, 0x39,
+ 0x13, 0x03, 0xca, 0xad, 0x5f, 0xd2, 0x57, 0x23,
+ 0x19, 0xdd, 0x71, 0xdd, 0x93, 0xe1, 0x3e, 0x43,
+ 0xaf, 0xdd, 0x94, 0x07, 0xf3, 0x78, 0xb3, 0x2a,
+ 0x57, 0x24, 0x97, 0x04, 0x58, 0xc1, 0xaf, 0xd3,
+ 0xe7, 0xa7, 0x65, 0xd1, 0x23, 0xa3, 0x93, 0x18,
+ 0xc7, 0x52, 0x70, 0x53, 0x60, 0x8b, 0x5a, 0x5d,
+ 0x6e, 0xf9, 0x83, 0x52, 0x99, 0xbb, 0x0a, 0x53,
+ 0x0e, 0x2a, 0x7f, 0x81, 0x52, 0x02, 0x32, 0xa4,
+ 0xfc, 0xe0, 0x17, 0x0c, 0x0e, 0x96, 0xbd, 0x01,
+ 0x29, 0x29, 0x29,
+};
+
+extern gcry_pk_spec_t _gcry_pubkey_spec_rsa;
+
+static void
+rsa_sexp_test (void)
+{
+ gcry_sexp_t sign_parms, sign_parms_invalid, pubkey, sig;
+ int rc;
+ grub_size_t errof;
+
+ rc = _gcry_sexp_build (&sign_parms_invalid, &errof,
+ "(data (flags) (value \"hi\"))\n");
+ grub_test_assert (rc == 0, "sexp build failed");
+
+ rc = _gcry_sexp_build (&sign_parms, &errof,
+ "(data (flags) (value \"hello\"))\n");
+ grub_test_assert (rc == 0, "sexp build failed");
+ rc = _gcry_sexp_new (&pubkey, pubkey_dump, sizeof(pubkey_dump), 0);
+ grub_test_assert (rc == 0, "sexp new failed");
+ rc = _gcry_sexp_new (&sig, sig_dump, sizeof(sig_dump), 0);
+ grub_test_assert (rc == 0, "sexp new failed");
+ rc = _gcry_pubkey_spec_rsa.verify (sig, sign_parms, pubkey);
+ grub_test_assert (rc == 0, "signature verification failed: %d", rc);
+ rc = _gcry_pubkey_spec_rsa.verify (sig, sign_parms_invalid, pubkey);
+ grub_test_assert (rc != 0, "signature verification succeded wrongly");
+}
+
+GRUB_FUNCTIONAL_TEST (rsa_sexp_test, rsa_sexp_test);

View File

@ -0,0 +1,47 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Vladimir Serbinenko <phcoder@gmail.com>
Date: Mon, 7 Jul 2025 14:52:12 +0000
Subject: [PATCH] keccak: Disable acceleration with SSE asm
Libgcrypt code assumes that on x64 all SSE registers are fair game.
While it's true that CPUs in question support it, we disable it in
our compilation options. Disable the offending optimization.
Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
.../lib/libgcrypt-patches/02_keccak_sse.patch | 25 ++++++++++++++++++++++
1 file changed, 25 insertions(+)
create mode 100644 grub-core/lib/libgcrypt-patches/02_keccak_sse.patch
diff --git a/grub-core/lib/libgcrypt-patches/02_keccak_sse.patch b/grub-core/lib/libgcrypt-patches/02_keccak_sse.patch
new file mode 100644
index 0000000..4deda12
--- /dev/null
+++ b/grub-core/lib/libgcrypt-patches/02_keccak_sse.patch
@@ -0,0 +1,25 @@
+commit b0cf06271da5fe20360953a53a47c69da89669cd
+Author: Vladimir Serbinenko <phcoder@gmail.com>
+Date: Sun Apr 7 03:33:11 2024 +0000
+
+ keccak: Disable acceleration with SSE asm
+
+ Libgcrypt code assumes that on x64 all SSE registers are fair game.
+ While it's true that CPUs in question support it, we disable it in
+ our compilation options. Disable the offending optimization.
+
+ Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
+
+diff --git a/grub-core/lib/libgcrypt/cipher/keccak.c b/grub-core/lib/libgcrypt/cipher/keccak.c
+index 11e64b3e7..8b570263b 100644
+--- a/grub-core/lib/libgcrypt-grub/cipher/keccak.c
++++ b/grub-core/lib/libgcrypt-grub/cipher/keccak.c
+@@ -275,7 +275,7 @@ keccak_absorb_lane32bi(u32 *lane, u32 x0, u32 x1)
+ /* Construct generic 64-bit implementation. */
+ #ifdef USE_64BIT
+
+-#if __GNUC__ >= 4 && defined(__x86_64__)
++#if __GNUC__ >= 4 && defined(__x86_64__) && 0
+
+ static inline void absorb_lanes64_8(u64 *dst, const byte *in)
+ {

View File

@ -0,0 +1,79 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Vladimir Serbinenko <phcoder@gmail.com>
Date: Mon, 7 Jul 2025 14:52:13 +0000
Subject: [PATCH] libgcrypt: Fix Coverity warnings
Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
.../lib/libgcrypt-patches/03_mpiutil_alloc.patch | 18 +++++++++++
grub-core/lib/libgcrypt-patches/03_sexp_free.patch | 35 ++++++++++++++++++++++
2 files changed, 53 insertions(+)
create mode 100644 grub-core/lib/libgcrypt-patches/03_mpiutil_alloc.patch
create mode 100644 grub-core/lib/libgcrypt-patches/03_sexp_free.patch
diff --git a/grub-core/lib/libgcrypt-patches/03_mpiutil_alloc.patch b/grub-core/lib/libgcrypt-patches/03_mpiutil_alloc.patch
new file mode 100644
index 0000000..42c6b2b
--- /dev/null
+++ b/grub-core/lib/libgcrypt-patches/03_mpiutil_alloc.patch
@@ -0,0 +1,18 @@
+mpiutil: Fix NULL pointer dereference in case of failed alloc
+
+Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
+
+diff --git a/grub-core/lib/libgcrypt/mpi/mpiutil.c b/grub-core/lib/libgcrypt/mpi/mpiutil.c
+index 3a372374f..dc53db09d 100644
+--- a/grub-core/lib/libgcrypt-grub/mpi/mpiutil.c
++++ b/grub-core/lib/libgcrypt-grub/mpi/mpiutil.c
+@@ -432,6 +432,9 @@ _gcry_mpi_alloc_like( gcry_mpi_t a )
+ int n = (a->sign+7)/8;
+ void *p = _gcry_is_secure(a->d)? xtrymalloc_secure (n)
+ : xtrymalloc (n);
++ if ( !p ) {
++ _gcry_fatal_error (GPG_ERR_ENOMEM, NULL);
++ }
+ memcpy( p, a->d, n );
+ b = mpi_set_opaque( NULL, p, a->sign );
+ }
diff --git a/grub-core/lib/libgcrypt-patches/03_sexp_free.patch b/grub-core/lib/libgcrypt-patches/03_sexp_free.patch
new file mode 100644
index 0000000..1c0ffd6
--- /dev/null
+++ b/grub-core/lib/libgcrypt-patches/03_sexp_free.patch
@@ -0,0 +1,35 @@
+sexp: Add missing free on error path
+
+Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
+
+diff --git a/grub-core/lib/libgcrypt/src/sexp.c b/grub-core/lib/libgcrypt/src/sexp.c
+index d15f1a790..250559f75 100644
+--- a/grub-core/lib/libgcrypt-grub/src/sexp.c
++++ b/grub-core/lib/libgcrypt-grub/src/sexp.c
+@@ -1157,6 +1157,17 @@ do_vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
+ } \
+ } while (0)
+
++#define MAKE_SPACE_EXTRA_CLEANUP(n, cleanup) do { \
++ gpg_err_code_t _ms_err = make_space (&c, (n)); \
++ if (_ms_err) \
++ { \
++ err = _ms_err; \
++ *erroff = p - buffer; \
++ cleanup; \
++ goto leave; \
++ } \
++ } while (0)
++
+ /* The STORE_LEN macro is used to store the length N at buffer P. */
+ #define STORE_LEN(p,n) do { \
+ DATALEN ashort = (n); \
+@@ -1368,7 +1379,7 @@ do_vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
+ goto leave;
+ }
+
+- MAKE_SPACE (datalen);
++ MAKE_SPACE_EXTRA_CLEANUP (datalen, xfree (b64buf));
+ *c.pos++ = ST_DATA;
+ STORE_LEN (c.pos, datalen);
+ for (i = 0; i < datalen; i++)

View File

@ -0,0 +1,40 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Vladimir Serbinenko <phcoder@gmail.com>
Date: Mon, 7 Jul 2025 14:52:14 +0000
Subject: [PATCH] libgcrypt: Remove now unneeded compilation flag
HAVE_STRTOUL is now defined in stdlib.h. Include it in g10lib.h rather
than defining on command line.
Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
conf/Makefile.common | 2 +-
util/import_gcry.py | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/conf/Makefile.common b/conf/Makefile.common
index c11ab27..5344a08 100644
--- a/conf/Makefile.common
+++ b/conf/Makefile.common
@@ -85,7 +85,7 @@ CFLAGS_POSIX = -fno-builtin
CPPFLAGS_POSIX = -I$(top_srcdir)/grub-core/lib/posix_wrap
CFLAGS_GCRY = -Wno-error=sign-compare -Wno-missing-field-initializers -Wno-redundant-decls -Wno-undef $(CFLAGS_POSIX)
-CPPFLAGS_GCRY = -I$(top_srcdir)/grub-core/lib/libgcrypt_wrap $(CPPFLAGS_POSIX) -D_GCRYPT_IN_LIBGCRYPT=1 -D_GCRYPT_CONFIG_H_INCLUDED=1 -DHAVE_STRTOUL=1 -I$(top_srcdir)/include/grub/gcrypt
+CPPFLAGS_GCRY = -I$(top_srcdir)/grub-core/lib/libgcrypt_wrap $(CPPFLAGS_POSIX) -D_GCRYPT_IN_LIBGCRYPT=1 -D_GCRYPT_CONFIG_H_INCLUDED=1 -I$(top_srcdir)/include/grub/gcrypt
CPPFLAGS_EFIEMU = -I$(top_srcdir)/grub-core/efiemu/runtime
diff --git a/util/import_gcry.py b/util/import_gcry.py
index 489356d..1be18cf 100644
--- a/util/import_gcry.py
+++ b/util/import_gcry.py
@@ -617,6 +617,7 @@ for src in sorted (os.listdir (os.path.join (indir, "src"))):
if src == "g10lib.h":
fw.write("#include <cipher_wrap.h>\n")
fw.write("#include <grub/crypto.h>\n")
+ fw.write("#include <stdlib.h>\n")
fw.write (f.read ().replace ("(printf,f,a)", "(__printf__,f,a)").replace ("#include \"../compat/libcompat.h\"", "").replace("#define N_(a) (a)", ""))
f.close ()
fw.close ()

View File

@ -0,0 +1,90 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Vladimir Serbinenko <phcoder@gmail.com>
Date: Mon, 7 Jul 2025 14:52:15 +0000
Subject: [PATCH] libgcrypt: Ignore sign-compare warnings
libgcrypt itself is compiled with -Wno-sign-compare. Do the same for consistency.
Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
conf/Makefile.common | 2 +-
grub-core/Makefile.core.def | 10 +++++-----
util/import_gcry.py | 2 +-
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/conf/Makefile.common b/conf/Makefile.common
index 5344a08..f25abeb 100644
--- a/conf/Makefile.common
+++ b/conf/Makefile.common
@@ -84,7 +84,7 @@ CPPFLAGS_GNULIB = -I$(top_builddir)/grub-core/lib/gnulib -I$(top_srcdir)/grub-co
CFLAGS_POSIX = -fno-builtin
CPPFLAGS_POSIX = -I$(top_srcdir)/grub-core/lib/posix_wrap
-CFLAGS_GCRY = -Wno-error=sign-compare -Wno-missing-field-initializers -Wno-redundant-decls -Wno-undef $(CFLAGS_POSIX)
+CFLAGS_GCRY = -Wno-sign-compare -Wno-missing-field-initializers -Wno-redundant-decls -Wno-undef $(CFLAGS_POSIX)
CPPFLAGS_GCRY = -I$(top_srcdir)/grub-core/lib/libgcrypt_wrap $(CPPFLAGS_POSIX) -D_GCRYPT_IN_LIBGCRYPT=1 -D_GCRYPT_CONFIG_H_INCLUDED=1 -I$(top_srcdir)/include/grub/gcrypt
CPPFLAGS_EFIEMU = -I$(top_srcdir)/grub-core/efiemu/runtime
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 0b6b996..ba4ed96 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1012,7 +1012,7 @@ module = {
module = {
name = pgp;
common = commands/pgp.c;
- cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls -Wno-sign-compare';
+ cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls';
cppflags = '$(CPPFLAGS_GCRY)';
};
@@ -2219,7 +2219,7 @@ module = {
name = dsa_sexp_test;
common = tests/dsa_sexp_test.c;
- cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls -Wno-sign-compare';
+ cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls';
cppflags = '$(CPPFLAGS_GCRY)';
};
@@ -2227,7 +2227,7 @@ module = {
name = rsa_sexp_test;
common = tests/rsa_sexp_test.c;
- cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls -Wno-sign-compare';
+ cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls';
cppflags = '$(CPPFLAGS_GCRY)';
};
@@ -2607,7 +2607,7 @@ module = {
common = lib/libgcrypt_wrap/mem.c;
common = lib/libgcrypt-grub/cipher/md.c;
- cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls -Wno-sign-compare -Wno-unused-but-set-variable';
+ cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls -Wno-unused-but-set-variable';
cppflags = '$(CPPFLAGS_GCRY)';
};
@@ -2619,7 +2619,7 @@ module = {
common = lib/libgcrypt-grub/src/sexp.c;
common = lib/b64dec.c;
- cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls -Wno-sign-compare';
+ cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls';
cppflags = '$(CPPFLAGS_GCRY)';
};
diff --git a/util/import_gcry.py b/util/import_gcry.py
index 1be18cf..a4411ec 100644
--- a/util/import_gcry.py
+++ b/util/import_gcry.py
@@ -551,7 +551,7 @@ for cipher_file in cipher_files:
confutil.write (" common = grub-core/lib/libgcrypt-grub/cipher/%s;\n" % src)
if modname == "gcry_ecc":
conf.write (" common = lib/libgcrypt-grub/mpi/ec.c;\n")
- conf.write (" cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls -Wno-sign-compare';\n")
+ conf.write (" cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls';\n")
elif modname == "gcry_rijndael" or modname == "gcry_md4" or modname == "gcry_md5" or modname == "gcry_rmd160" or modname == "gcry_sha1" or modname == "gcry_sha256" or modname == "gcry_sha512" or modname == "gcry_tiger":
# Alignment checked by hand
conf.write (" cflags = '$(CFLAGS_GCRY) -Wno-cast-align';\n");

View File

@ -0,0 +1,217 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Vladimir Serbinenko <phcoder@gmail.com>
Date: Mon, 7 Jul 2025 14:52:16 +0000
Subject: [PATCH] libgcrypt: Import blake family of hashes
Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/lib/libgcrypt-patches/06_blake.patch | 80 ++++++++++++++++++++++++++
include/grub/crypto.h | 9 +++
util/import_gcry.py | 36 +++++++++---
3 files changed, 118 insertions(+), 7 deletions(-)
create mode 100644 grub-core/lib/libgcrypt-patches/06_blake.patch
diff --git a/grub-core/lib/libgcrypt-patches/06_blake.patch b/grub-core/lib/libgcrypt-patches/06_blake.patch
new file mode 100644
index 0000000..c3b9d6f
--- /dev/null
+++ b/grub-core/lib/libgcrypt-patches/06_blake.patch
@@ -0,0 +1,80 @@
+--- a/grub-core/lib/libgcrypt-grub/cipher/blake2.c
++++ b/grub-core/lib/libgcrypt-grub/cipher/blake2.c
+@@ -841,68 +841,6 @@
+ return blake2s_init(c, key, keylen);
+ }
+
+-/* Selftests from "RFC 7693, Appendix E. BLAKE2b and BLAKE2s Self-Test
+- * Module C Source". */
+-static void selftest_seq(byte *out, size_t len, u32 seed)
+-{
+- size_t i;
+- u32 t, a, b;
+-
+- a = 0xDEAD4BAD * seed;
+- b = 1;
+-
+- for (i = 0; i < len; i++)
+- {
+- t = a + b;
+- a = b;
+- b = t;
+- out[i] = (t >> 24) & 0xFF;
+- }
+-}
+-
+-
+-
+-
+-gcry_err_code_t _gcry_blake2_init_with_key(void *ctx, unsigned int flags,
+- const unsigned char *key,
+- size_t keylen, int algo)
+-{
+- gcry_err_code_t rc;
+- switch (algo)
+- {
+- case GCRY_MD_BLAKE2B_512:
+- rc = blake2b_init_ctx (ctx, flags, key, keylen, 512);
+- break;
+- case GCRY_MD_BLAKE2B_384:
+- rc = blake2b_init_ctx (ctx, flags, key, keylen, 384);
+- break;
+- case GCRY_MD_BLAKE2B_256:
+- rc = blake2b_init_ctx (ctx, flags, key, keylen, 256);
+- break;
+- case GCRY_MD_BLAKE2B_160:
+- rc = blake2b_init_ctx (ctx, flags, key, keylen, 160);
+- break;
+- case GCRY_MD_BLAKE2S_256:
+- rc = blake2s_init_ctx (ctx, flags, key, keylen, 256);
+- break;
+- case GCRY_MD_BLAKE2S_224:
+- rc = blake2s_init_ctx (ctx, flags, key, keylen, 224);
+- break;
+- case GCRY_MD_BLAKE2S_160:
+- rc = blake2s_init_ctx (ctx, flags, key, keylen, 160);
+- break;
+- case GCRY_MD_BLAKE2S_128:
+- rc = blake2s_init_ctx (ctx, flags, key, keylen, 128);
+- break;
+- default:
+- rc = GPG_ERR_DIGEST_ALGO;
+- break;
+- }
+-
+- return rc;
+-}
+-
+-
+ #define DEFINE_BLAKE2_VARIANT(bs, BS, dbits, oid_branch) \
+ static void blake2##bs##_##dbits##_init(void *ctx, unsigned int flags) \
+ { \
+@@ -936,7 +874,7 @@
+ dbits / 8, blake2##bs##_##dbits##_init, blake2##bs##_write, \
+ blake2##bs##_final, blake2##bs##_read, NULL, \
+ _gcry_blake2##bs##_##dbits##_hash_buffers, \
+- sizeof (BLAKE2##BS##_CONTEXT), selftests_blake2##bs \
++ sizeof (BLAKE2##BS##_CONTEXT) \
+ , \
+ GRUB_UTIL_MODNAME("gcry_blake2") \
+ .blocksize = GRUB_BLAKE2 ## BS ## _BLOCK_SIZE \
diff --git a/include/grub/crypto.h b/include/grub/crypto.h
index 25b1188..b0d7add 100644
--- a/include/grub/crypto.h
+++ b/include/grub/crypto.h
@@ -586,8 +586,14 @@ void grub_gcry_fini_all (void);
int
grub_get_random (void *out, grub_size_t len);
+#define GRUB_UTIL_MODNAME(x) .modname = x,
+#else
+#define GRUB_UTIL_MODNAME(x)
#endif
+#define GRUB_BLAKE2B_BLOCK_SIZE 128
+#define GRUB_BLAKE2S_BLOCK_SIZE 64
+
typedef struct _gpgrt_b64state *gpgrt_b64state_t;
gpgrt_b64state_t gpgrt_b64dec_start (const char *title);
gpg_error_t gpgrt_b64dec_proc (gpgrt_b64state_t state,
@@ -595,4 +601,7 @@ gpg_error_t gpgrt_b64dec_proc (gpgrt_b64state_t state,
grub_size_t *r_nbytes);
gpg_error_t gpgrt_b64dec_finish (gpgrt_b64state_t state);
const char *gpg_strerror (gpg_error_t err);
+
+gcry_err_code_t blake2b_vl_hash (const void *in, grub_size_t inlen,
+ grub_size_t outputlen, void *output);
#endif
diff --git a/util/import_gcry.py b/util/import_gcry.py
index a4411ec..ca918c7 100644
--- a/util/import_gcry.py
+++ b/util/import_gcry.py
@@ -120,7 +120,8 @@ mdblocksizes = {"_gcry_digest_spec_crc32" : 64,
"_gcry_digest_spec_gost3411_94": 32,
"_gcry_digest_spec_gost3411_cp": 32,
"_gcry_digest_spec_cshake128": 64,
- "_gcry_digest_spec_cshake256": 64}
+ "_gcry_digest_spec_cshake256": 64,
+ "_gcry_digest_spec_blake2": "GRUB_BLAKE2 ## BS ## _BLOCK_SIZE"}
cryptolist = codecs.open (os.path.join (cipher_dir_out, "crypto.lst"), "w", "utf-8")
@@ -209,6 +210,7 @@ for cipher_file in cipher_files:
skip = 0
skip2 = False
ismd = False
+ ismddefine = False
mdarg = 0
ispk = False
iscipher = False
@@ -245,19 +247,19 @@ for cipher_file in cipher_files:
mdarg = mdarg + len (spl) - 1
if ismd or iscipher or ispk:
if not re.search (" *};", line) is None:
+ escapenl = " \\" if ismddefine else ""
if not iscomma:
- fw.write (" ,\n")
- fw.write ("#ifdef GRUB_UTIL\n");
- fw.write (" .modname = \"%s\",\n" % modname);
- fw.write ("#endif\n");
+ fw.write (f" ,{escapenl}\n")
+ fw.write (f" GRUB_UTIL_MODNAME(\"%s\"){escapenl}\n" % modname);
if ismd:
if not (mdname in mdblocksizes):
print ("ERROR: Unknown digest blocksize: %s\n"
% mdname)
exit (1)
- fw.write (" .blocksize = %s\n"
+ fw.write (f" .blocksize = %s{escapenl}\n"
% mdblocksizes [mdname])
ismd = False
+ ismddefine = False
mdarg = 0
iscipher = False
ispk = False
@@ -281,7 +283,7 @@ for cipher_file in cipher_files:
hold = False
# We're optimising for size and exclude anything needing good
# randomness.
- if re.match ("(_gcry_hash_selftest_check_one|bulk_selftest_setkey|run_selftests|do_tripledes_set_extra_info|selftest|sm4_selftest|_gcry_[a-z0-9_]*_hash_buffers|_gcry_sha1_hash_buffer|tripledes_set2keys|_gcry_rmd160_mixblock|serpent_test|dsa_generate_ext|test_keys|gen_k|sign|gen_x931_parm_xp|generate_x931|generate_key|dsa_generate|dsa_sign|ecc_sign|generate|generate_fips186|_gcry_register_pk_dsa_progress|_gcry_register_pk_ecc_progress|progress|scanval|ec2os|ecc_generate_ext|ecc_generate|ecc_get_param|_gcry_register_pk_dsa_progress|gen_x931_parm_xp|gen_x931_parm_xi|rsa_decrypt|rsa_sign|rsa_generate_ext|rsa_generate|secret|check_exponent|rsa_blind|rsa_unblind|extract_a_from_sexp|curve_free|curve_copy|point_set|_gcry_dsa_gen_rfc6979_k|bits2octets|int2octets|_gcry_md_debug|_gcry_md_selftest|_gcry_md_is_enabled|_gcry_md_is_secure|_gcry_md_init|_gcry_md_info|md_get_algo|md_extract|_gcry_md_get |_gcry_md_get_algo |_gcry_md_extract|_gcry_md_setkey|md_setkey|prepare_macpads|_gcry_md_algo_name|search_oid|spec_from_oid|spec_from_name|spec_from_algo|map_algo|cshake_hash_buffers)", line) is not None:
+ if re.match ("(_gcry_hash_selftest_check_one|bulk_selftest_setkey|run_selftests|do_tripledes_set_extra_info|selftest|sm4_selftest|_gcry_[a-z0-9_]*_hash_buffers|_gcry_sha1_hash_buffer|tripledes_set2keys|_gcry_rmd160_mixblock|serpent_test|dsa_generate_ext|test_keys|gen_k|sign|gen_x931_parm_xp|generate_x931|generate_key|dsa_generate|dsa_sign|ecc_sign|generate|generate_fips186|_gcry_register_pk_dsa_progress|_gcry_register_pk_ecc_progress|progress|scanval|ec2os|ecc_generate_ext|ecc_generate|ecc_get_param|_gcry_register_pk_dsa_progress|gen_x931_parm_xp|gen_x931_parm_xi|rsa_decrypt|rsa_sign|rsa_generate_ext|rsa_generate|secret|check_exponent|rsa_blind|rsa_unblind|extract_a_from_sexp|curve_free|curve_copy|point_set|_gcry_dsa_gen_rfc6979_k|bits2octets|int2octets|_gcry_md_debug|_gcry_md_selftest|_gcry_md_is_enabled|_gcry_md_is_secure|_gcry_md_init|_gcry_md_info|md_get_algo|md_extract|_gcry_md_get |_gcry_md_get_algo |_gcry_md_extract|_gcry_md_setkey|md_setkey|prepare_macpads|_gcry_md_algo_name|search_oid|spec_from_oid|spec_from_name|spec_from_algo|map_algo|cshake_hash_buffers|selftest_seq)", line) is not None:
skip = 1
if not re.match ("selftest", line) is None and cipher_file == "idea.c":
@@ -355,6 +357,13 @@ for cipher_file in cipher_files:
ispk = True
iscryptostart = True
+ m = re.match (r"DEFINE_BLAKE2_VARIANT\((.), (.), ([0-9]*)", line)
+ if isc and not m is None:
+ bs = m.groups()[0]
+ bits = m.groups()[2]
+ mdname = f"_gcry_digest_spec_blake2{bs}_{bits}"
+ mdnames.append (mdname)
+
m = re.match ("(const )?gcry_md_spec_t", line)
if isc and not m is None:
assert (not ismd)
@@ -366,6 +375,19 @@ for cipher_file in cipher_files:
mdname = re.match("[a-zA-Z0-9_]*",mdname).group ()
mdnames.append (mdname)
ismd = True
+ ismddefine = False
+ mdarg = 0
+ iscryptostart = True
+ m = re.match (" (const )?gcry_md_spec_t _gcry_digest_spec_blake2.*\\\\", line)
+ if isc and not m is None:
+ assert (not ismd)
+ assert (not ispk)
+ assert (not iscipher)
+ assert (not iscryptostart)
+ line = removeprefix(line, " const ")
+ ismd = True
+ ismddefine = True
+ mdname = "_gcry_digest_spec_blake2"
mdarg = 0
iscryptostart = True
m = re.match (r"static const char \*selftest.*;$", line)

View File

@ -0,0 +1,44 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Vladimir Serbinenko <phcoder@gmail.com>
Date: Mon, 7 Jul 2025 14:52:17 +0000
Subject: [PATCH] util/import_gcry: Make compatible with Python 3.4
Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
util/import_gcry.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/util/import_gcry.py b/util/import_gcry.py
index ca918c7..9b0e8d9 100644
--- a/util/import_gcry.py
+++ b/util/import_gcry.py
@@ -249,15 +249,15 @@ for cipher_file in cipher_files:
if not re.search (" *};", line) is None:
escapenl = " \\" if ismddefine else ""
if not iscomma:
- fw.write (f" ,{escapenl}\n")
- fw.write (f" GRUB_UTIL_MODNAME(\"%s\"){escapenl}\n" % modname);
+ fw.write (" ,%s\n" % escapenl)
+ fw.write (" GRUB_UTIL_MODNAME(\"%s\")%s\n" % (modname, escapenl))
if ismd:
if not (mdname in mdblocksizes):
print ("ERROR: Unknown digest blocksize: %s\n"
% mdname)
exit (1)
- fw.write (f" .blocksize = %s{escapenl}\n"
- % mdblocksizes [mdname])
+ fw.write (" .blocksize = %s%s\n"
+ % (mdblocksizes [mdname], escapenl))
ismd = False
ismddefine = False
mdarg = 0
@@ -361,7 +361,7 @@ for cipher_file in cipher_files:
if isc and not m is None:
bs = m.groups()[0]
bits = m.groups()[2]
- mdname = f"_gcry_digest_spec_blake2{bs}_{bits}"
+ mdname = "_gcry_digest_spec_blake2%s_%s" % (bs, bits)
mdnames.append (mdname)
m = re.match ("(const )?gcry_md_spec_t", line)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,34 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Vladimir Serbinenko <phcoder@gmail.com>
Date: Mon, 7 Jul 2025 14:52:19 +0000
Subject: [PATCH] libgcrypt: Don't use 64-bit division on platforms where it's
slow
Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/lib/libgcrypt-patches/07_disable_64div.patch | 15 +++++++++++++++
1 file changed, 15 insertions(+)
create mode 100644 grub-core/lib/libgcrypt-patches/07_disable_64div.patch
diff --git a/grub-core/lib/libgcrypt-patches/07_disable_64div.patch b/grub-core/lib/libgcrypt-patches/07_disable_64div.patch
new file mode 100644
index 0000000..bb47bdf
--- /dev/null
+++ b/grub-core/lib/libgcrypt-patches/07_disable_64div.patch
@@ -0,0 +1,15 @@
+Don't use 64-bit division on platforms where it's slow
+
+diff --git a/grub-core/lib/libgcrypt/mpi/longlong.h b/grub-core/lib/libgcrypt/mpi/longlong.h
+index 21bd1a7ef..672448724 100644
+--- a/grub-core/lib/libgcrypt-grub/mpi/longlong.h
++++ b/grub-core/lib/libgcrypt-grub/mpi/longlong.h
+@@ -1711,7 +1711,7 @@ typedef unsigned int UTItype __attribute__ ((mode (TI)));
+ } while (0)
+
+ /* Use double word type if available. */
+-#if !defined (udiv_qrnnd) && defined (UDWtype)
++#if !defined (udiv_qrnnd) && defined (UDWtype) && !defined(__arm__) && !defined(__mips__) && !defined(__powerpc__)
+ # define udiv_qrnnd(q, r, nh, nl, d) \
+ do { \
+ UWtype __d = (d); \

View File

@ -0,0 +1,41 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Vladimir Serbinenko <phcoder@gmail.com>
Date: Mon, 7 Jul 2025 14:52:20 +0000
Subject: [PATCH] libgcrypt: Fix a memory leak
Fixes: CID 468917
Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/lib/libgcrypt-patches/08_sexp_leak.patch | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
create mode 100644 grub-core/lib/libgcrypt-patches/08_sexp_leak.patch
diff --git a/grub-core/lib/libgcrypt-patches/08_sexp_leak.patch b/grub-core/lib/libgcrypt-patches/08_sexp_leak.patch
new file mode 100644
index 0000000..eefd031
--- /dev/null
+++ b/grub-core/lib/libgcrypt-patches/08_sexp_leak.patch
@@ -0,0 +1,21 @@
+sexp: Fix a memory leak
+
+Fixes: CID 468917
+
+Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
+
+diff -ur ../libgcrypt-1.11.0/src/sexp.c grub-core/lib/libgcrypt/src/sexp.c
+--- a/grub-core/lib/libgcrypt-grub/src/sexp.c 2024-03-28 10:07:27.000000000 +0000
++++ b/grub-core/lib/libgcrypt-grub/src/sexp.c 2025-07-02 17:10:32.714864459 +0000
+@@ -2725,8 +2725,10 @@
+ length = 0;
+ for (s=string; *s; s +=2 )
+ {
+- if (!hexdigitp (s) || !hexdigitp (s+1))
++ if (!hexdigitp (s) || !hexdigitp (s+1)) {
++ free (buffer);
+ return NULL; /* Invalid hex digits. */
++ }
+ ((unsigned char*)buffer)[length++] = xtoi_2 (s);
+ }
+ *r_length = length;

View File

@ -0,0 +1,67 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Vladimir Serbinenko <phcoder@gmail.com>
Date: Mon, 7 Jul 2025 14:52:21 +0000
Subject: [PATCH] docs: Write how to import new libgcrypt
Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
docs/grub-dev.texi | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
index 8ad5494..090cc88 100644
--- a/docs/grub-dev.texi
+++ b/docs/grub-dev.texi
@@ -506,6 +506,7 @@ to update it.
* Gnulib::
* jsmn::
* minilzo::
+* libgcrypt::
@end menu
@node Gnulib
@@ -596,6 +597,43 @@ cp minilzo-2.10/*.[hc] grub-core/lib/minilzo
rm -r minilzo-2.10*
@end example
+@node libgcrypt
+@section libgcrypt
+
+libgcrypt is a GNU implementation of crypto library. To import a new version
+you need to unpack the release tarball into grub-core/lib/libgcrypt. Delete
+following files/directories:
+* acinclude.m4
+* aclocal.m4
+* autogen.rc
+* autogen.sh
+* build-aux
+* ChangeLog
+* ChangeLog-2011
+* doc
+* INSTALL
+* m4
+* Makefile.am
+* Makefile.in
+* NEWS
+* random
+* tests
+* TODO
+* */Makefile.in
+* mpi/hppa1.1
+
+Regenerate the file gost-sb.h:
+grub-core/lib/libgcrypt/cipher$ gcc -o gost-s-box gost-s-box.c
+grub-core/lib/libgcrypt/cipher$ ./gost-s-box gost-sb.h
+
+Then rerun ./bootstrap and pay attention to the errors. Especially to warnings
+that a file isn't a module as it means that some file is actually unused by
+GRUB. If any, find where it declares its cipher or hash and add a pattern to
+import_gcry.py. See commit ``libgcrypt: Import blake family of hashes'' for an
+example. If file reallly is useless to GRUB in its current state, add it to
+whitelist in import_gcry.py.
+Compile and fix any new errors. Put patches into grub-core/lib/libgcrypt-patches
+
@node Debugging
@chapter Debugging

View File

@ -0,0 +1,36 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Date: Mon, 6 Oct 2025 12:54:48 +0530
Subject: [PATCH] pgp: Rename OBJ_TYPE_PUBKEY to OBJ_TYPE_GPG_PUBKEY
Prior to the addition of the X.509 public key support for appended signature,
current PGP signature relied on the GPG public key. Changing the enum name
from "OBJ_TYPE_PUBKEY" to "OBJ_TYPE_GPG_PUBKEY" to differentiate between x509
certificate based appended signature and GPG certificate based PGP signature.
Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
Signed-off-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
include/grub/kernel.h | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/include/grub/kernel.h b/include/grub/kernel.h
index f98a780..8fdbde2 100644
--- a/include/grub/kernel.h
+++ b/include/grub/kernel.h
@@ -28,10 +28,9 @@ enum
OBJ_TYPE_MEMDISK,
OBJ_TYPE_CONFIG,
OBJ_TYPE_PREFIX,
- OBJ_TYPE_PUBKEY,
+ OBJ_TYPE_GPG_PUBKEY,
OBJ_TYPE_DTB,
OBJ_TYPE_DISABLE_SHIM_LOCK,
- OBJ_TYPE_GPG_PUBKEY,
OBJ_TYPE_X509_PUBKEY,
OBJ_TYPE_DISABLE_CLI
};

View File

@ -0,0 +1,210 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Date: Mon, 6 Oct 2025 12:54:49 +0530
Subject: [PATCH] grub-install: Support embedding x509 certificates
To support verification of appended signatures, we need a way to embed the
necessary public keys. Existing appended signature schemes in the Linux kernel
use X.509 certificates, so allow certificates to be embedded in the GRUB core
image in the same way as PGP keys.
Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
Signed-off-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
include/grub/kernel.h | 2 +-
include/grub/util/install.h | 9 ++++-----
util/grub-install-common.c | 18 ++++++++----------
util/grub-mkimage.c | 6 +++---
util/mkimage.c | 38 +++++++++++++++++++++-----------------
5 files changed, 37 insertions(+), 36 deletions(-)
diff --git a/include/grub/kernel.h b/include/grub/kernel.h
index 8fdbde2b0613..05f141201c61 100644
--- a/include/grub/kernel.h
+++ b/include/grub/kernel.h
@@ -29,9 +29,9 @@ enum
OBJ_TYPE_CONFIG,
OBJ_TYPE_PREFIX,
OBJ_TYPE_GPG_PUBKEY,
+ OBJ_TYPE_X509_PUBKEY,
OBJ_TYPE_DTB,
OBJ_TYPE_DISABLE_SHIM_LOCK,
- OBJ_TYPE_X509_PUBKEY,
OBJ_TYPE_DISABLE_CLI
};
diff --git a/include/grub/util/install.h b/include/grub/util/install.h
index dbf3c216d413..93c1f0ed4e0d 100644
--- a/include/grub/util/install.h
+++ b/include/grub/util/install.h
@@ -67,13 +67,12 @@
N_("SBAT metadata"), 0 }, \
{ "disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0, \
N_("disable shim_lock verifier"), 0 }, \
- { "x509key", 'x', N_("FILE"), 0, \
- N_("embed FILE as an x509 certificate for 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}, \
{ "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 }
diff --git a/util/grub-install-common.c b/util/grub-install-common.c
index 42aec141e444..41251cee798b 100644
--- a/util/grub-install-common.c
+++ b/util/grub-install-common.c
@@ -463,10 +463,10 @@ handle_install_list (struct install_list *il, const char *val,
static char **pubkeys;
static size_t npubkeys;
-static char *sbat;
-static int disable_shim_lock;
static char **x509keys;
static size_t nx509keys;
+static char *sbat;
+static int disable_shim_lock;
static grub_compression_t compression;
static size_t appsig_size;
static int disable_cli;
@@ -509,15 +509,13 @@ grub_install_parse (int key, char *arg)
case GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK:
disable_shim_lock = 1;
return 1;
- case 'x':
- x509keys = xrealloc (x509keys,
- sizeof (x509keys[0])
- * (nx509keys + 1));
- x509keys[nx509keys++] = xstrdup (arg);
- return 1;
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++;
@@ -649,7 +647,7 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
slen += sizeof (" --pubkey ''") + grub_strlen (*pk);
for (pk = x509keys; pk < x509keys + nx509keys; pk++)
- slen += 10 + grub_strlen (*pk);
+ slen += sizeof (" --x509key ''") + grub_strlen (*pk);
for (md = modules.entries; *md; md++)
slen += sizeof (" ''") + grub_strlen (*md);
@@ -693,7 +691,7 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
for (pk = x509keys; pk < x509keys + nx509keys; pk++)
{
- p = grub_stpcpy (p, "--x509 '");
+ p = grub_stpcpy (p, "--x509key '");
p = grub_stpcpy (p, *pk);
*p++ = '\'';
*p++ = ' ';
diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c
index 13bdc6cf0397..89ca81ce6b10 100644
--- a/util/grub-mkimage.c
+++ b/util/grub-mkimage.c
@@ -76,7 +76,7 @@ static struct argp_option options[] = {
{"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 PGP signature checking"), 0},
- {"x509", 'x', N_("FILE"), 0, N_("embed FILE as an x509 certificate for appended 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},
@@ -213,11 +213,11 @@ argp_parser (int key, char *arg, struct argp_state *state)
case 'x':
arguments->x509keys = xrealloc (arguments->x509keys,
- sizeof (arguments->x509keys[0])
- * (arguments->nx509keys + 1));
+ sizeof (arguments->x509keys[0]) * (arguments->nx509keys + 1));
arguments->x509keys[arguments->nx509keys++] = xstrdup (arg);
break;
+
case 'c':
if (arguments->config)
free (arguments->config);
diff --git a/util/mkimage.c b/util/mkimage.c
index f92949d1df25..8ae58ccef868 100644
--- a/util/mkimage.c
+++ b/util/mkimage.c
@@ -930,16 +930,21 @@ 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);
+ 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);
}
}
@@ -1078,24 +1083,23 @@ 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;
+ size_t curs;
+ struct grub_module_header *header;
- curs = grub_util_get_image_size (x509key_paths[i]);
+ 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));
- 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);
+ 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;

View File

@ -0,0 +1,162 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Date: Tue, 18 Nov 2025 16:14:23 +0100
Subject: [PATCH] appended signatures: Import GNUTLS's ASN.1 description files
In order to parse PKCS#7 messages and X.509 certificates with libtasn1, we need
some information about how they are encoded. We get these from GNUTLS, which has
the benefit that they support the features we need and are well tested.
The GNUTLS files are from:
- https://github.com/gnutls/gnutls/blob/master/lib/gnutls.asn
- https://github.com/gnutls/gnutls/blob/master/lib/pkix.asn
The GNUTLS license is LGPLv2.1+, which is GPLv3 compatible, allowing us to import
it without issue.
Signed-off-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/commands/appendedsig/gnutls_asn1_tab.c | 33 +++++++++++++++++++++---
grub-core/commands/appendedsig/pkix_asn1_tab.c | 27 +++++++++----------
2 files changed, 44 insertions(+), 16 deletions(-)
diff --git a/grub-core/commands/appendedsig/gnutls_asn1_tab.c b/grub-core/commands/appendedsig/gnutls_asn1_tab.c
index ddd1314..efc0c14 100644
--- a/grub-core/commands/appendedsig/gnutls_asn1_tab.c
+++ b/grub-core/commands/appendedsig/gnutls_asn1_tab.c
@@ -1,7 +1,11 @@
#include <grub/mm.h>
-#include <grub/libtasn1.h>
+#include <libtasn1.h>
-const asn1_static_node gnutls_asn1_tab[] = {
+/*
+ * 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 },
@@ -55,6 +59,9 @@ const asn1_static_node gnutls_asn1_tab[] = {
{ "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 },
@@ -86,6 +93,13 @@ const asn1_static_node gnutls_asn1_tab[] = {
{ "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 },
@@ -113,9 +127,22 @@ const asn1_static_node gnutls_asn1_tab[] = {
{ "ephemeralPublicKey", 1610637314, "SubjectPublicKeyInfo"},
{ NULL, 4104, "0"},
{ "ukm", 7, NULL },
- { "GostR3410-KeyTransport", 536870917, 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/pkix_asn1_tab.c b/grub-core/commands/appendedsig/pkix_asn1_tab.c
index adef69d..ec5f87b 100644
--- a/grub-core/commands/appendedsig/pkix_asn1_tab.c
+++ b/grub-core/commands/appendedsig/pkix_asn1_tab.c
@@ -1,7 +1,11 @@
#include <grub/mm.h>
-#include <grub/libtasn1.h>
+#include <libtasn1.h>
-const asn1_static_node pkix_asn1_tab[] = {
+/*
+ * 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 },
@@ -27,9 +31,7 @@ const asn1_static_node pkix_asn1_tab[] = {
{ "MAX", 524298, "1"},
{ "utf8String", 1612709922, NULL },
{ "MAX", 524298, "1"},
- { "bmpString", 1612709921, NULL },
- { "MAX", 524298, "1"},
- { "ia5String", 538968093, NULL },
+ { "bmpString", 538968097, NULL },
{ "MAX", 524298, "1"},
{ "SubjectAltName", 1073741826, "GeneralNames"},
{ "GeneralNames", 1612709899, NULL },
@@ -64,8 +66,7 @@ const asn1_static_node pkix_asn1_tab[] = {
{ "BasicConstraints", 1610612741, NULL },
{ "cA", 1610645508, NULL },
{ NULL, 131081, NULL },
- { "pathLenConstraint", 537411587, NULL },
- { "0", 10, "MAX"},
+ { "pathLenConstraint", 16387, NULL },
{ "CRLDistributionPoints", 1612709899, NULL },
{ "MAX", 1074266122, "1"},
{ NULL, 2, "DistributionPoint"},
@@ -277,14 +278,15 @@ const asn1_static_node pkix_asn1_tab[] = {
{ "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", 1611137027, NULL },
- { "1", 10, "MAX"},
- { "keyLength", 1611153411, NULL },
- { "1", 10, "MAX"},
+ { "iterationCount", 1073741827, NULL },
+ { "keyLength", 1073758211, NULL },
{ "prf", 16386, "AlgorithmIdentifier"},
{ "pkcs-12-PFX", 1610612741, NULL },
{ "version", 1610874883, NULL },
@@ -341,8 +343,7 @@ const asn1_static_node pkix_asn1_tab[] = {
{ "MAX", 1074266122, "1"},
{ NULL, 2, "Attribute"},
{ "ProxyCertInfo", 1610612741, NULL },
- { "pCPathLenConstraint", 1611153411, NULL },
- { "0", 10, "MAX"},
+ { "pCPathLenConstraint", 1073758211, NULL },
{ "proxyPolicy", 2, "ProxyPolicy"},
{ "ProxyPolicy", 1610612741, NULL },
{ "policyLanguage", 1073741836, NULL },

View File

@ -0,0 +1,274 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Date: Tue, 18 Nov 2025 16:18:32 +0100
Subject: [PATCH] appended signatures: Parse ASN1 node
This code allows us to parse ASN1 node and 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.
Signed-off-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/commands/appendedsig/appendedsig.h | 96 +++++-----------------------
grub-core/commands/appendedsig/asn1util.c | 59 ++++++++---------
2 files changed, 43 insertions(+), 112 deletions(-)
diff --git a/grub-core/commands/appendedsig/appendedsig.h b/grub-core/commands/appendedsig/appendedsig.h
index 9792ef3..601d616 100644
--- a/grub-core/commands/appendedsig/appendedsig.h
+++ b/grub-core/commands/appendedsig/appendedsig.h
@@ -1,6 +1,7 @@
/*
* GRUB -- GRand Unified Bootloader
- * Copyright (C) 2020 IBM Corporation.
+ * 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
@@ -16,95 +17,28 @@
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <grub/crypto.h>
-#include <grub/libtasn1.h>
+#include <libtasn1.h>
-extern asn1_node _gnutls_gnutls_asn;
-extern asn1_node _gnutls_pkix_asn;
+extern asn1_node grub_gnutls_gnutls_asn;
+extern asn1_node grub_gnutls_pkix_asn;
-#define MAX_OID_LEN 32
+/* Do libtasn1 init. */
+extern int
+grub_asn1_init (void);
/*
- * One or more x509 certificates.
- *
- * We do limited parsing: extracting only the serial, CN and RSA public key.
- */
-struct x509_certificate
-{
- struct x509_certificate *next;
-
- grub_uint8_t *serial;
- grub_size_t serial_len;
-
- char *subject;
- grub_size_t subject_len;
-
- /* We only support RSA public keys. This encodes [modulus, publicExponent] */
- gcry_mpi_t mpis[2];
-};
-
-/*
- * A PKCS#7 signedData message.
- *
- * We make no attempt to match intelligently, so we don't save any info about
- * the signer. We also support only 1 signerInfo, so we only store a single
- * MPI for the signature.
- */
-struct pkcs7_signedData
-{
- const gcry_md_spec_t *hash;
- gcry_mpi_t sig_mpi;
-};
-
-
-/* Do libtasn1 init */
-int asn1_init (void);
-
-/*
- * Import a DER-encoded certificate at 'data', of size 'size'.
- *
- * Place the results into 'results', which must be already allocated.
- */
-grub_err_t
-certificate_import (void *data, grub_size_t size,
- struct x509_certificate *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.
- */
-void certificate_release (struct x509_certificate *cert);
-
-/*
- * Parse a PKCS#7 message, which must be a signedData message.
- *
- * The message must be in 'sigbuf' and of size 'data_size'. The result is
- * placed in 'msg', which must already be allocated.
- */
-grub_err_t
-parse_pkcs7_signedData (void *sigbuf, grub_size_t data_size,
- struct pkcs7_signedData *msg);
-
-/*
- * Release all the storage associated with the PKCS#7 message.
- * If the caller dynamically allocated the message, it must free it.
- */
-void pkcs7_signedData_release (struct pkcs7_signedData *msg);
-
-/*
- * Read a value from an ASN1 node, allocating memory to store it.
- *
- * It will work for anything where the size libtasn1 returns is right:
+ * 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
+ * - 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,
- int *content_size);
+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
index eff095a..9dd7898 100644
--- a/grub-core/commands/appendedsig/asn1util.c
+++ b/grub-core/commands/appendedsig/asn1util.c
@@ -1,6 +1,7 @@
/*
* GRUB -- GRand Unified Bootloader
- * Copyright (C) 2020 IBM Corporation.
+ * 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
@@ -16,28 +17,29 @@
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <grub/libtasn1.h>
+#include <libtasn1.h>
#include <grub/types.h>
#include <grub/err.h>
#include <grub/mm.h>
#include <grub/crypto.h>
+#include <grub/misc.h>
#include <grub/gcrypt/gcrypt.h>
#include "appendedsig.h"
-asn1_node _gnutls_gnutls_asn = ASN1_TYPE_EMPTY;
-asn1_node _gnutls_pkix_asn = ASN1_TYPE_EMPTY;
+asn1_node grub_gnutls_gnutls_asn = NULL;
+asn1_node grub_gnutls_pkix_asn = NULL;
-extern const ASN1_ARRAY_TYPE gnutls_asn1_tab[];
-extern const ASN1_ARRAY_TYPE pkix_asn1_tab[];
+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:
+ * 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.
@@ -45,30 +47,26 @@ extern const ASN1_ARRAY_TYPE pkix_asn1_tab[];
* 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, int *content_size)
+grub_asn1_allocate_and_read (asn1_node node, const char *name, const char *friendly_name,
+ grub_int32_t *content_size)
{
- int result;
+ grub_int32_t result;
grub_uint8_t *tmpstr = NULL;
- int tmpstr_size = 0;
+ grub_int32_t tmpstr_size = 0;
result = asn1_read_value (node, name, NULL, &tmpstr_size);
if (result != ASN1_MEM_ERROR)
{
- grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
- _
- ("Reading size of %s did not return expected status: %s"),
- friendly_name, asn1_strerror (result));
- grub_errno = GRUB_ERR_BAD_FILE_TYPE;
+ 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_snprintf (grub_errmsg, sizeof (grub_errmsg),
- "Could not allocate memory to store %s", friendly_name);
- grub_errno = GRUB_ERR_OUT_OF_MEMORY;
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "could not allocate memory to store %s",
+ friendly_name) ;
return NULL;
}
@@ -76,10 +74,8 @@ grub_asn1_allocate_and_read (asn1_node node, const char *name,
if (result != ASN1_SUCCESS)
{
grub_free (tmpstr);
- grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
- "Error reading %s: %s",
- friendly_name, asn1_strerror (result));
- grub_errno = GRUB_ERR_BAD_FILE_TYPE;
+ grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading %s: %s", friendly_name,
+ asn1_strerror (result)) ;
return NULL;
}
@@ -89,14 +85,15 @@ grub_asn1_allocate_and_read (asn1_node node, const char *name,
}
int
-asn1_init (void)
+grub_asn1_init (void)
{
int res;
- res = asn1_array2tree (gnutls_asn1_tab, &_gnutls_gnutls_asn, NULL);
+
+ res = asn1_array2tree (grub_gnutls_asn1_tab, &grub_gnutls_gnutls_asn, NULL);
if (res != ASN1_SUCCESS)
- {
- return res;
- }
- res = asn1_array2tree (pkix_asn1_tab, &_gnutls_pkix_asn, NULL);
+ return res;
+
+ res = asn1_array2tree (grub_pkix_asn1_tab, &grub_gnutls_pkix_asn, NULL);
+
return res;
}

View File

@ -0,0 +1,659 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Date: Tue, 18 Nov 2025 16:20:56 +0100
Subject: [PATCH] appended signatures: Parse PKCS#7 signed data
This code allows us to parse:
- PKCS#7 signed data messages. Only a single signer info is supported, which
is all that the Linux sign-file utility supports creating out-of-the-box.
Only RSA, SHA-256 and SHA-512 are supported. Any certificate embedded in
the PKCS#7 message will be ignored.
Signed-off-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/commands/appendedsig/appendedsig.h | 37 +++
grub-core/commands/appendedsig/pkcs7.c | 439 ++++++++++++++++++---------
2 files changed, 330 insertions(+), 146 deletions(-)
diff --git a/grub-core/commands/appendedsig/appendedsig.h b/grub-core/commands/appendedsig/appendedsig.h
index 601d616..b0beb89 100644
--- a/grub-core/commands/appendedsig/appendedsig.h
+++ b/grub-core/commands/appendedsig/appendedsig.h
@@ -17,11 +17,48 @@
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <grub/crypto.h>
#include <libtasn1.h>
extern asn1_node grub_gnutls_gnutls_asn;
extern asn1_node grub_gnutls_pkix_asn;
+#define GRUB_MAX_OID_LEN 32
+
+/* 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;
+
+/*
+ * 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);
diff --git a/grub-core/commands/appendedsig/pkcs7.c b/grub-core/commands/appendedsig/pkcs7.c
index dc6afe2..b8e2720 100644
--- a/grub-core/commands/appendedsig/pkcs7.c
+++ b/grub-core/commands/appendedsig/pkcs7.c
@@ -1,6 +1,7 @@
/*
* GRUB -- GRand Unified Bootloader
- * Copyright (C) 2020 IBM Corporation.
+ * 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
@@ -20,57 +21,55 @@
#include <grub/misc.h>
#include <grub/crypto.h>
#include <grub/gcrypt/gcrypt.h>
-
+#include <sys/types.h>
static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
-/*
- * RFC 5652 s 5.1
- */
-const char *signedData_oid = "1.2.840.113549.1.7.2";
+/* RFC 5652 s 5.1. */
+static const char *signedData_oid = "1.2.840.113549.1.7.2";
-/*
- * RFC 4055 s 2.1
- */
-const char *sha256_oid = "2.16.840.1.101.3.4.2.1";
-const char *sha512_oid = "2.16.840.1.101.3.4.2.3";
+/* 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, int size,
- struct pkcs7_signedData *msg)
+process_content (grub_uint8_t *content, grub_int32_t size, grub_pkcs7_data_t *msg)
{
- int res;
+ grub_int32_t res;
asn1_node signed_part;
grub_err_t err = GRUB_ERR_NONE;
- char algo_oid[MAX_OID_LEN];
- int algo_oid_size = sizeof (algo_oid);
- int algo_count;
+ 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;
- int version_size = sizeof (version);
+ grub_int32_t version_size = sizeof (version);
grub_uint8_t *result_buf;
- int result_size = 0;
- int crls_size = 0;
+ 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 (_gnutls_pkix_asn, "PKIX1.pkcs-7-SignedData",
- &signed_part);
+ 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.");
- }
+ 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);
+ 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);
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "error reading PKCS#7 signed data: %s", asn1_error);
goto cleanup_signed_part;
}
- /* SignedData ::= SEQUENCE {
+ /*
+ * SignedData ::= SEQUENCE {
* version CMSVersion,
* digestAlgorithms DigestAlgorithmIdentifiers,
* encapContentInfo EncapsulatedContentInfo,
@@ -79,23 +78,19 @@ process_content (grub_uint8_t * content, int size,
* signerInfos SignerInfos }
*/
- /* version per the algo in 5.1, must be 1 */
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));
+ 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);
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "unexpected signature version v%d, only v1 supported", version);
goto cleanup_signed_part;
}
@@ -104,147 +99,301 @@ process_content (grub_uint8_t * content, int size,
*
* 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);
+ 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));
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE, "error counting number of digest algorithms: %s",
+ asn1_strerror (res));
goto cleanup_signed_part;
}
- if (algo_count != 1)
+ if (algo_count <= 0)
{
- err =
- grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
- "Only 1 digest algorithm is supported");
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE, "a minimum of 1 digest algorithm is required");
goto cleanup_signed_part;
}
- res =
- asn1_read_value (signed_part, "digestAlgorithms.?1.algorithm", algo_oid,
- &algo_oid_size);
- if (res != ASN1_SUCCESS)
+ if (algo_count > 2)
{
- err =
- grub_error (GRUB_ERR_BAD_SIGNATURE,
- "Error reading digest algorithm: %s",
- asn1_strerror (res));
+ err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "a maximum of 2 digest algorithms is supported");
goto cleanup_signed_part;
}
- if (grub_strncmp (sha512_oid, algo_oid, algo_oid_size) == 0)
- {
- msg->hash = grub_crypto_lookup_md_by_name ("sha512");
- }
- else if (grub_strncmp (sha256_oid, algo_oid, algo_oid_size) == 0)
- {
- msg->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_signed_part;
- }
+ sha256_in_da = false;
+ sha512_in_da = false;
- if (!msg->hash)
+ for (i = 0; i < algo_count; i++)
{
- err =
- grub_error (GRUB_ERR_BAD_SIGNATURE,
- "Hash algorithm for OID %s not loaded", algo_oid);
- goto cleanup_signed_part;
+ 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.
+ * 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");
+ err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "PKCS#7 messages with embedded CRLs are not supported");
goto cleanup_signed_part;
}
- /* read the signature */
- result_buf =
- grub_asn1_allocate_and_read (signed_part, "signerInfos.?1.signature",
- "signature data", &result_size);
- if (!result_buf)
+ /* Read the signatures */
+ res = asn1_number_of_elements (signed_part, "signerInfos", &signer_count);
+ if (res != ASN1_SUCCESS)
{
- err = grub_errno;
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE, "error counting number of signers: %s",
+ asn1_strerror (res));
goto cleanup_signed_part;
}
- gcry_err =
- gcry_mpi_scan (&(msg->sig_mpi), GCRYMPI_FMT_USG, result_buf, result_size,
- NULL);
- if (gcry_err != GPG_ERR_NO_ERROR)
+ if (signer_count <= 0)
{
- err =
- grub_error (GRUB_ERR_BAD_SIGNATURE,
- "Error loading signature into MPI structure: %d",
- gcry_err);
- goto cleanup_result;
+ 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;
}
-cleanup_result:
- grub_free (result_buf);
-cleanup_signed_part:
+ 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
-parse_pkcs7_signedData (void *sigbuf, grub_size_t data_size,
- struct pkcs7_signedData *msg)
+grub_pkcs7_data_parse (const void *sigbuf, grub_size_t data_size, grub_pkcs7_data_t *msg)
{
- int res;
+ grub_int32_t res;
asn1_node content_info;
grub_err_t err = GRUB_ERR_NONE;
- char content_oid[MAX_OID_LEN];
+ char content_oid[GRUB_MAX_OID_LEN];
grub_uint8_t *content;
- int content_size;
- int content_oid_size = sizeof (content_oid);
- int size;
+ 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_INT_MAX)
+ if (data_size > GRUB_UINT_MAX)
return grub_error (GRUB_ERR_OUT_OF_RANGE,
- "Cannot parse a PKCS#7 message where data size > INT_MAX");
- size = (int) data_size;
+ "cannot parse a PKCS#7 message where data size > GRUB_UINT_MAX");
- res = asn1_create_element (_gnutls_pkix_asn,
- "PKIX1.pkcs-7-ContentInfo", &content_info);
+ 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));
- }
+ 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_error);
+ 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);
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "error decoding PKCS#7 message DER: %s", asn1_error);
goto cleanup;
}
@@ -255,32 +404,24 @@ parse_pkcs7_signedData (void *sigbuf, grub_size_t data_size,
*
* ContentType ::= OBJECT IDENTIFIER
*/
- res =
- asn1_read_value (content_info, "contentType", content_oid,
- &content_oid_size);
+ 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));
+ 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 */
+ /* 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);
+ 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)
+ content = grub_asn1_allocate_and_read (content_info, "content", "PKCS#7 message content", &content_size);
+ if (content == NULL)
{
err = grub_errno;
goto cleanup;
@@ -289,17 +430,23 @@ parse_pkcs7_signedData (void *sigbuf, grub_size_t data_size,
err = process_content (content, content_size, msg);
grub_free (content);
-cleanup:
+ 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.
+ * Release all the storage associated with the PKCS#7 message. If the caller
+ * dynamically allocated the message, it must free it.
*/
void
-pkcs7_signedData_release (struct pkcs7_signedData *msg)
+grub_pkcs7_data_release (grub_pkcs7_data_t *msg)
{
- gcry_mpi_release (msg->sig_mpi);
+ grub_int32_t i;
+
+ for (i = 0; i < msg->signer_count; i++)
+ _gcry_mpi_release (msg->signers[i].sig_mpi);
+
+ grub_free (msg->signers);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,122 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Date: Mon, 6 Oct 2025 12:54:54 +0530
Subject: [PATCH] powerpc/ieee1275: Enter lockdown based on /ibm, secure-boot
Read secure boot mode from 'ibm,secure-boot' property and if the secure boot
mode is set to 2 (enforce), enter lockdown. Else it is considered as disabled.
There are three secure boot modes. They are
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.
Signed-off-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/kern/ieee1275/init.c | 56 +++++++++++++++++++++++++++++++-----------
1 file changed, 42 insertions(+), 14 deletions(-)
diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
index 482cad2..0c587d3 100644
--- a/grub-core/kern/ieee1275/init.c
+++ b/grub-core/kern/ieee1275/init.c
@@ -49,7 +49,14 @@
#if defined(__powerpc__) || defined(__i386__)
#include <grub/ieee1275/alloc.h>
#endif
+#if defined(__powerpc__)
#include <grub/lockdown.h>
+#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. Not used by sparc. */
#ifdef __i386__
@@ -1009,30 +1016,49 @@ grub_parse_cmdline (void)
}
}
+#ifdef __powerpc__
static void
-grub_get_ieee1275_secure_boot (void)
+grub_ieee1275_get_secure_boot (void)
{
grub_ieee1275_phandle_t root;
- int rc;
- grub_uint32_t is_sb;
+ grub_uint32_t sb_mode = GRUB_SB_DISABLED;
+ grub_int32_t rc;
- grub_ieee1275_finddevice ("/", &root);
+ 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", &is_sb,
- sizeof (is_sb), 0);
-
- /* ibm,secure-boot:
+ 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
- * 3 - enforce + OS-specific behaviour
+ * Lockdown the GRUB. Signature verification is performed and If
+ * signature verification fails, display the errors and stop the boot.
*
- * We only support enforce.
+ * Now, only support disabled and enforce.
*/
- if (rc >= 0 && is_sb >= 2)
- grub_lockdown ();
+ if (sb_mode == GRUB_SB_ENFORCE)
+ {
+ grub_dprintf ("ieee1275", "Secure Boot Enabled\n");
+ grub_lockdown ();
+ }
+ else
+ grub_dprintf ("ieee1275", "Secure Boot Disabled\n");
}
-
+#endif /* __powerpc__ */
grub_addr_t grub_modbase;
void
@@ -1059,7 +1085,9 @@ grub_machine_init (void)
grub_install_get_time_ms (grub_rtc_get_time_ms);
#endif
- grub_get_ieee1275_secure_boot ();
+#ifdef __powerpc__
+ grub_ieee1275_get_secure_boot ();
+#endif
}
void

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,775 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Date: Mon, 6 Oct 2025 12:54:56 +0530
Subject: [PATCH] powerpc/ieee1275: Read the db and dbx secure boot variables
Enhancing the infrastructure to enable the Platform Keystore (PKS) feature,
which provides access to the SB_VERSION, db, and dbx secure boot variables
from PKS.
If PKS is enabled, it will read secure boot variables such as db and dbx
from PKS and extract EFI Signature List (ESL) from it. The ESLs would be
saved in the Platform Keystore buffer, and the appendedsig module would
read it later to extract the certificate's details from ESL.
In the following scenarios, static key management mode will be activated:
1. When Secure Boot is enabled with static key management mode
2. When SB_VERSION is unavailable but Secure Boot is enabled
3. When PKS support is unavailable but Secure Boot is enabled
Note:
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.
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/Makefile.am | 2 +
grub-core/Makefile.core.def | 2 +
grub-core/kern/ieee1275/ieee1275.c | 1 -
grub-core/kern/ieee1275/init.c | 4 +
grub-core/kern/powerpc/ieee1275/ieee1275.c | 137 +++++++++
.../kern/powerpc/ieee1275/platform_keystore.c | 333 +++++++++++++++++++++
include/grub/ieee1275/ieee1275.h | 3 +
include/grub/powerpc/ieee1275/ieee1275.h | 36 +++
include/grub/powerpc/ieee1275/platform_keystore.h | 123 ++++++++
9 files changed, 640 insertions(+), 1 deletion(-)
create mode 100644 grub-core/kern/powerpc/ieee1275/ieee1275.c
create mode 100644 grub-core/kern/powerpc/ieee1275/platform_keystore.c
create mode 100644 include/grub/powerpc/ieee1275/platform_keystore.h
diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
index aa17239..8dd7014 100644
--- a/grub-core/Makefile.am
+++ b/grub-core/Makefile.am
@@ -247,6 +247,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 15721c5..e16013a 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -338,6 +338,8 @@ kernel = {
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;
diff --git a/grub-core/kern/ieee1275/ieee1275.c b/grub-core/kern/ieee1275/ieee1275.c
index 36ca2db..afa37a9 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 0c587d3..32ee281 100644
--- a/grub-core/kern/ieee1275/init.c
+++ b/grub-core/kern/ieee1275/init.c
@@ -51,6 +51,8 @@
#endif
#if defined(__powerpc__)
#include <grub/lockdown.h>
+#include <grub/powerpc/ieee1275/ieee1275.h>
+#include <grub/powerpc/ieee1275/platform_keystore.h>
#endif
#ifdef __powerpc__
@@ -1057,6 +1059,8 @@ grub_ieee1275_get_secure_boot (void)
}
else
grub_dprintf ("ieee1275", "Secure Boot Disabled\n");
+
+ grub_pks_keystore_init ();
}
#endif /* __powerpc__ */
grub_addr_t grub_modbase;
diff --git a/grub-core/kern/powerpc/ieee1275/ieee1275.c b/grub-core/kern/powerpc/ieee1275/ieee1275.c
new file mode 100644
index 0000000..20c49e3
--- /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 <http://www.gnu.org/licenses/>.
+ */
+#include <grub/ieee1275/ieee1275.h>
+#include <grub/powerpc/ieee1275/ieee1275.h>
+#include <grub/misc.h>
+
+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 0000000..cc2d493
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/lockdown.h>
+#include <grub/ieee1275/ieee1275.h>
+#include <grub/powerpc/ieee1275/ieee1275.h>
+#include <grub/powerpc/ieee1275/platform_keystore.h>
+
+/* 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/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h
index db0ec5f..17aecf2 100644
--- a/include/grub/ieee1275/ieee1275.h
+++ b/include/grub/ieee1275/ieee1275.h
@@ -24,6 +24,9 @@
#include <grub/types.h>
#include <grub/machine/ieee1275.h>
+#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/powerpc/ieee1275/ieee1275.h b/include/grub/powerpc/ieee1275/ieee1275.h
index 4eb2070..4b9966d 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 0000000..931ada2
--- /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 <grub/symbol.h>
+#include <grub/mm.h>
+#include <grub/types.h>
+
+/*
+ * 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

View File

@ -0,0 +1,144 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Date: Mon, 6 Oct 2025 12:54:57 +0530
Subject: [PATCH] appended signatures: Introducing key management environment
variable
Introducing the 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".
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/commands/appendedsig/appendedsig.c | 75 ++++++++++++++++++++++++++++
1 file changed, 75 insertions(+)
diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c
index e53efd2..ca54c90 100644
--- a/grub-core/commands/appendedsig/appendedsig.c
+++ b/grub-core/commands/appendedsig/appendedsig.c
@@ -33,6 +33,7 @@
#include <libtasn1.h>
#include <grub/env.h>
#include <grub/lockdown.h>
+#include <grub/powerpc/ieee1275/platform_keystore.h>
#include "appendedsig.h"
@@ -94,6 +95,16 @@ static sb_database_t db = {.certs = NULL, .cert_entries = 0};
*/
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;
+
static grub_ssize_t
pseudo_read (struct grub_file *file, char *buf, grub_size_t len)
{
@@ -469,6 +480,46 @@ grub_env_write_sec (struct grub_env_var *var __attribute__ ((unused)), const cha
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)
@@ -540,6 +591,11 @@ GRUB_MOD_INIT (appendedsig)
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
@@ -554,6 +610,23 @@ GRUB_MOD_INIT (appendedsig)
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));
@@ -577,5 +650,7 @@ GRUB_MOD_FINI (appendedsig)
free_db_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);
}

View File

@ -0,0 +1,689 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Date: Mon, 6 Oct 2025 12:54:58 +0530
Subject: [PATCH] appended signatures: Create db and dbx lists
If secure boot is enabled with static key management mode, the trusted
certificates will be extracted from the GRUB ELF Note and added to db list.
If secure boot is enabled with dynamic key management mode, the trusted
certificates and certificate/binary hash will be extracted from the PKS
and added to db list. The distrusted certificates, certificate/binary hash
are read from the PKS and added to dbx list. Both dbx and db lists usage is
added by a subsequent patch.
Note:
- 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 the certificate or the certificate hash exists in the dbx list, then do not
add that certificate/certificate hash to the db list.
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/commands/appendedsig/appendedsig.c | 407 +++++++++++++++++++++++++--
include/grub/efi/pks.h | 112 ++++++++
include/grub/types.h | 4 +
3 files changed, 506 insertions(+), 17 deletions(-)
create mode 100644 include/grub/efi/pks.h
diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c
index ca54c90..0c4c788 100644
--- a/grub-core/commands/appendedsig/appendedsig.c
+++ b/grub-core/commands/appendedsig/appendedsig.c
@@ -34,6 +34,7 @@
#include <grub/env.h>
#include <grub/lockdown.h>
#include <grub/powerpc/ieee1275/platform_keystore.h>
+#include <grub/efi/pks.h>
#include "appendedsig.h"
@@ -46,6 +47,11 @@ GRUB_MOD_LICENSE ("GPLv3+");
#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
+
/*
* This structure is extracted from scripts/sign-file.c in the linux kernel
* source. It was licensed as LGPLv2.1+, which is GPLv3+ compatible.
@@ -79,11 +85,23 @@ 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};
+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).
@@ -118,6 +136,169 @@ static struct grub_fs pseudo_fs = {
.fs_read = pseudo_read
};
+/*
+ * 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)
{
@@ -136,7 +317,33 @@ is_cert_match (const grub_x509_cert_t *cert1, const grub_x509_cert_t *cert2)
return false;
}
-/* Check the certificate presence in the db list. */
+/* 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)
{
@@ -149,7 +356,11 @@ check_cert_presence (const grub_x509_cert_t *cert_in, const sb_database_t *sb_da
return false;
}
-/* Add the certificate into the db list */
+/*
+ * 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)
@@ -167,30 +378,54 @@ add_certificate (const grub_uint8_t *data, const grub_size_t data_size,
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 db list\n",
- cert->subject);
+ 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 db list",
- cert->subject);
- grub_x509_cert_release (cert);
- grub_free (cert);
-
- return GRUB_ERR_EXISTS;
+ "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 db list\n",
- cert->subject);
+ 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;
}
@@ -382,6 +617,68 @@ grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize)
return err;
}
+/* 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.
@@ -422,11 +719,45 @@ load_elf2db (void)
}
}
+/*
+ * 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)
{
@@ -436,9 +767,37 @@ free_db_list (void)
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)))
@@ -631,10 +990,23 @@ GRUB_MOD_INIT (appendedsig)
if (rc != ASN1_SUCCESS)
grub_fatal ("error initing ASN.1 data structures: %d: %s\n", rc, asn1_strerror (rc));
- /* Extract trusted keys from ELF Note and store them in the db. */
- load_elf2db ();
- grub_dprintf ("appendedsig", "the db list now has %u static keys\n",
- db.cert_entries);
+ /*
+ * 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);
+ }
grub_verifier_register (&grub_appendedsig_verifier);
grub_dl_set_persistent (mod);
@@ -648,6 +1020,7 @@ GRUB_MOD_FINI (appendedsig)
*/
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);
diff --git a/include/grub/efi/pks.h b/include/grub/efi/pks.h
new file mode 100644
index 0000000..ff306f5
--- /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 <grub/types.h>
+
+/*
+ * 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/types.h b/include/grub/types.h
index 59e0302..5f37adb 100644
--- a/include/grub/types.h
+++ b/include/grub/types.h
@@ -380,6 +380,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;
@@ -389,4 +391,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 */

View File

@ -0,0 +1,136 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Date: Mon, 6 Oct 2025 12:54:59 +0530
Subject: [PATCH] appended signatures: Using db and dbx lists for signature
verification
Signature verification: verify the kernel against lists of hashes that are
either in dbx or db list. If it is not in the dbx list then the trusted keys
from the db list are used to verify the signature.
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/commands/appendedsig/appendedsig.c | 94 +++++++++++++++++++++++++++-
1 file changed, 93 insertions(+), 1 deletion(-)
diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c
index 0c4c788..9cfa1be 100644
--- a/grub-core/commands/appendedsig/appendedsig.c
+++ b/grub-core/commands/appendedsig/appendedsig.c
@@ -521,6 +521,83 @@ extract_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize,
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.
@@ -565,7 +642,7 @@ grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize)
grub_pkcs7_signer_t *si;
grub_int32_t i;
- if (!db.cert_entries)
+ 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);
@@ -574,6 +651,21 @@ grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize)
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++)
{

View File

@ -0,0 +1,406 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Date: Mon, 6 Oct 2025 12:55:00 +0530
Subject: [PATCH] appended signatures: GRUB commands to manage the certificates
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Introducing the following GRUB commands to manage the certificates.
1. append_list_db:
Show the list of trusted certificates from the db list
2. append_add_db_cert:
Add the trusted certificate to the db list
3. append_add_dbx_cert:
Add the distrusted certificate to the dbx list
4. append_verify:
Verify the signed file using db list
Note that if signature verification (check_appended_signatures) is set to yes,
the append_add_db_cert and append_add_dbx_cert commands only accept the file
X509_certificate that is signed with an appended signature.
Signed-off-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Tested-by: Sridhar Markonda <sridharm@linux.ibm.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/commands/appendedsig/appendedsig.c | 316 +++++++++++++++++++++++++++
1 file changed, 316 insertions(+)
diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c
index 9cfa1be..614ebee 100644
--- a/grub-core/commands/appendedsig/appendedsig.c
+++ b/grub-core/commands/appendedsig/appendedsig.c
@@ -123,6 +123,9 @@ 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 grub_ssize_t
pseudo_read (struct grub_file *file, char *buf, grub_size_t len)
{
@@ -136,6 +139,65 @@ static struct grub_fs pseudo_fs = {
.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.
@@ -429,6 +491,61 @@ add_certificate (const grub_uint8_t *data, const grub_size_t data_size,
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 grub_err_t
file_read_whole (grub_file_t file, grub_uint8_t **buf, grub_size_t *len)
{
@@ -649,6 +766,7 @@ grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize)
if (err != GRUB_ERR_NONE)
return err;
+ append_sig_len = sig.signature_len;
datasize = bufsize - sig.signature_len;
/*
@@ -709,6 +827,189 @@ grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize)
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 <SIGNED FILE>\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 <X509_CERTIFICATE>\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 <X509_CERTIFICATE>\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;
+}
+
/* Add the X.509 certificates/binary hash to the db list from PKS. */
static grub_err_t
load_pks2db (void)
@@ -1031,6 +1332,8 @@ struct grub_file_verifier grub_appendedsig_verifier = {
.write = appendedsig_write,
};
+static grub_command_t cmd_verify, cmd_list_db, cmd_dbx_cert, cmd_db_cert;
+
GRUB_MOD_INIT (appendedsig)
{
grub_int32_t rc;
@@ -1100,6 +1403,15 @@ GRUB_MOD_INIT (appendedsig)
db.cert_entries);
}
+ cmd_verify = grub_register_command ("append_verify", grub_cmd_verify_signature, N_("<SIGNED_FILE>"),
+ 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_("<X509_CERTIFICATE>"),
+ N_("Add trusted X509_CERTIFICATE to the db list"));
+ cmd_dbx_cert = grub_register_command ("append_add_dbx_cert", grub_cmd_dbx_cert, N_("<X509_CERTIFICATE>"),
+ N_("Add distrusted X509_CERTIFICATE to the dbx list"));
+
grub_verifier_register (&grub_appendedsig_verifier);
grub_dl_set_persistent (mod);
}
@@ -1118,4 +1430,8 @@ GRUB_MOD_FINI (appendedsig)
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);
}

View File

@ -0,0 +1,382 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Date: Mon, 6 Oct 2025 12:55:01 +0530
Subject: [PATCH] appended signatures: GRUB commands to manage the hashes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Introducing the following GRUB commands to manage certificate/binary
hashes.
1. append_list_dbx:
Show the list of distrusted certificates and binary/certificate
hashes from the dbx list.
2. append_add_db_hash:
Add the trusted binary hash to the db list.
3. append_add_dbx_hash:
Add the distrusted certificate/binary hash to the dbx list.
Note that if signature verification (check_appended_signatures) is set to yes,
the append_add_db_hash and append_add_dbx_hash commands only accept the file
hash_file that is signed with an appended signature.
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Tested-by: Sridhar Markonda <sridharm@linux.ibm.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/commands/appendedsig/appendedsig.c | 279 +++++++++++++++++++++++++++
include/grub/file.h | 2 +
2 files changed, 281 insertions(+)
diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c
index 614ebee..5c53f63 100644
--- a/grub-core/commands/appendedsig/appendedsig.c
+++ b/grub-core/commands/appendedsig/appendedsig.c
@@ -52,6 +52,9 @@ GRUB_MOD_LICENSE ("GPLv3+");
#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.
@@ -126,6 +129,13 @@ 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)
{
@@ -546,6 +556,69 @@ remove_cert_from_db (const grub_uint8_t *data, const grub_size_t data_size)
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)
{
@@ -1010,6 +1083,192 @@ grub_cmd_list_db (grub_command_t cmd __attribute__ ((unused)), int argc __attrib
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 <BINARY HASH FILE>\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] <FILE>\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)
@@ -1292,6 +1551,11 @@ appendedsig_init (grub_file_t io __attribute__ ((unused)), enum grub_file_type t
* 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:
/*
@@ -1333,6 +1597,8 @@ struct grub_file_verifier grub_appendedsig_verifier = {
};
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)
{
@@ -1412,6 +1678,16 @@ GRUB_MOD_INIT (appendedsig)
cmd_dbx_cert = grub_register_command ("append_add_dbx_cert", grub_cmd_dbx_cert, N_("<X509_CERTIFICATE>"),
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);
}
@@ -1434,4 +1710,7 @@ GRUB_MOD_FINI (appendedsig)
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/include/grub/file.h b/include/grub/file.h
index d678de0..16a4b7d 100644
--- a/include/grub/file.h
+++ b/include/grub/file.h
@@ -115,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. */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,44 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Daniel Axtens <dja@axtens.net>
Date: Mon, 6 Oct 2025 12:55:03 +0530
Subject: [PATCH] docs/grub: Document signing GRUB under UEFI
Before adding information about how GRUB is signed with an appended
signature scheme, it's worth adding some information about how it
can currently be signed for UEFI.
Signed-off-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
docs/grub.texi | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/docs/grub.texi b/docs/grub.texi
index 2d6d73a..e08aaf5 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -6912,6 +6912,21 @@ 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 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.
+@section 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.
+
@node Platform limitations
@chapter Platform limitations

View File

@ -0,0 +1,124 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Date: Mon, 6 Oct 2025 12:55:04 +0530
Subject: [PATCH] docs/grub: Document signing GRUB with an appended signature
Signing GRUB for firmware that verifies an appended signature is a
bit fiddly. I don't want people to have to figure it out from scratch
so document it here.
Signed-off-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
docs/grub.texi | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 95 insertions(+)
diff --git a/docs/grub.texi b/docs/grub.texi
index e08aaf5..0b1c9d1 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -6927,6 +6927,101 @@ 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.
+@section 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 Platform limitations
@chapter Platform limitations

View File

@ -0,0 +1,465 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Date: Mon, 6 Oct 2025 12:55:05 +0530
Subject: [PATCH] docs/grub: Document appended signature
This explains how appended signatures can be used to form part of
a secure boot chain, and documents the commands and variables
introduced.
Signed-off-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
docs/grub.texi | 355 +++++++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 293 insertions(+), 62 deletions(-)
diff --git a/docs/grub.texi b/docs/grub.texi
index 0b1c9d1..e4f36df 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -3274,6 +3274,7 @@ GRUB. Others may be used freely in GRUB configuration files.
These variables have special meaning to GRUB.
@menu
+* appendedsig_key_mgmt::
* biosnum::
* check_appended_signatures::
* check_signatures::
@@ -3327,6 +3328,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
@@ -3339,13 +3353,17 @@ this.
For an alternative approach which also changes BIOS drive mappings for the
chain-loaded system, @pxref{drivemap}.
-
@node check_appended_signatures
@subsection check_appended_signatures
This variable controls whether GRUB enforces appended signature validation on
-certain loaded files. @xref{Using appended signatures}.
+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
@@ -4350,6 +4368,13 @@ 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
@@ -4469,6 +4494,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 <X509_certificate>
+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 <hash_file>
+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 <X509_certificate>
+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}] <hash_file>
+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 <signed_file>
+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
@@ -5139,10 +5298,12 @@ configurations, and to enable ``one-shot'' boot attempts and
``savedefault'' behavior. @xref{Using GPG-style digital signatures}, for more
information.
-Extra care should be taken when combining this command with appended signatures
-(@pxref{Using appended signatures}), as this file is not validated by an
-appended signature and could set @code{check_appended_signatures=no} if GRUB is
-not in @pxref{Lockdown} mode.
+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
@@ -6488,6 +6649,7 @@ environment variables and commands are listed in the same order.
* Secure Boot Advanced Targeting:: Embedded information for generation number based revocation
* Measured Boot:: Measuring boot components
* Lockdown:: Lockdown when booting on a secure setup
+* Signing certificate and hash files:: Certificate and hash file signing
* Signing GRUB itself:: Ensuring the integrity of the GRUB core image
@end menu
@@ -6661,8 +6823,8 @@ secure boot chain.
@node Using appended signatures
@section Using appended signatures in GRUB
-GRUB supports verifying Linux-style 'appended signatures' for secure boot.
-Appended signatures are PKCS#7 messages containing a signature over the
+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:
@@ -6670,73 +6832,114 @@ with an appended signature ends with the magic string:
~Module signature appended~\n
@end example
-where @code{\n} represents the line-feed character, @code{0x0a}.
+where @code{\n} represents the line feed character, @code{0x0a}.
-Certificates can be managed at boot time using the @pxref{trust_certificate},
-@pxref{distrust_certificate} and @pxref{list_certificates} commands.
-Certificates can also be built in to the core image using the @code{--x509}
-parameter to @command{grub-install} or @command{grub-mkimage}.
+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
-A file can be explictly verified using the @pxref{verify_appended} command.
+@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
-Only signatures made with the SHA-256 or SHA-512 hash algorithm are supported,
-and only RSA signatures are supported.
+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 x509 certificate containing the public key:
+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
-Enforcement of signature verification is controlled by the
-@code{check_appended_signatures} variable.
+Once signature verification is turned on, the following file types must carry
+appended signatures:
-@itemize
-@item @samp{no}: no verification is performed. This is the default when GRUB
- is not in @pxref{Lockdown} mode.
-@item @samp{enforce}: verification is performed. Verification can be disabled
- by setting the variable back to @samp{no}.
-@item @samp{forced}: verification is performed and cannot be disabled. This is
- set when GRUB is in Lockdown when the appendedsig module is loaded.
-@end itemize
-
-Unlike GPG-style signatures, not all files loaded by GRUB are required to be
-signed. Once verification is turned on, the following file types will have
-appended signatures verified:
-
-@itemize
+@enumerate
@item Linux kernels
-@item GRUB modules, except those built into the core image
-@item Any new certificate files to be trusted
-@end itemize
-
-ACPI tables and Device Tree images will not be checked for appended signatures
-but must be verified by another mechanism such as GPG-style signatures before
-they will be loaded.
-
-Unless lockdown mode is enabled, signature checking does @strong{not}
-stop an attacker with console access from dropping manually to the GRUB
-console and executing:
-
-@example
-set check_appended_signatures=no
-@end example
-
-Refer to the section on password-protecting GRUB (@pxref{Authentication
-and authorisation}) for more information on preventing this.
-
-Additionally, unless lockdown mode is enabled:
-
-@itemize
-@item Special care must be taken around the @command{loadenv} command, which
- can be used to turn off @code{check_appended_signature}.
-
-@item If the grub configuration file is loaded from the disk, anyone who can
- modify the file on disk can turn off @code{check_appended_signature}.
- Consider embedding the configuration into the core grub image.
-@end itemize
+@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
@@ -6912,6 +7115,34 @@ 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

View File

@ -0,0 +1,89 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nicolas Frayer <nfrayer@redhat.com>
Date: Wed, 19 Nov 2025 13:33:12 +0100
Subject: [PATCH] libtasn1: Fix include path for grub
Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
---
grub-core/commands/appendedsig/appendedsig.c | 2 +-
grub-core/commands/appendedsig/appendedsig.h | 2 +-
grub-core/commands/appendedsig/asn1util.c | 2 +-
grub-core/commands/appendedsig/gnutls_asn1_tab.c | 2 +-
grub-core/commands/appendedsig/pkix_asn1_tab.c | 2 +-
grub-core/commands/appendedsig/x509.c | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c
index 5c53f63..1c74554 100644
--- a/grub-core/commands/appendedsig/appendedsig.c
+++ b/grub-core/commands/appendedsig/appendedsig.c
@@ -30,7 +30,7 @@
#include <grub/kernel.h>
#include <grub/extcmd.h>
#include <grub/verify.h>
-#include <libtasn1.h>
+#include <grub/libtasn1.h>
#include <grub/env.h>
#include <grub/lockdown.h>
#include <grub/powerpc/ieee1275/platform_keystore.h>
diff --git a/grub-core/commands/appendedsig/appendedsig.h b/grub-core/commands/appendedsig/appendedsig.h
index c874654..cb0fe19 100644
--- a/grub-core/commands/appendedsig/appendedsig.h
+++ b/grub-core/commands/appendedsig/appendedsig.h
@@ -18,7 +18,7 @@
*/
#include <grub/crypto.h>
-#include <libtasn1.h>
+#include <grub/libtasn1.h>
extern asn1_node grub_gnutls_gnutls_asn;
extern asn1_node grub_gnutls_pkix_asn;
diff --git a/grub-core/commands/appendedsig/asn1util.c b/grub-core/commands/appendedsig/asn1util.c
index 9dd7898..1f50a74 100644
--- a/grub-core/commands/appendedsig/asn1util.c
+++ b/grub-core/commands/appendedsig/asn1util.c
@@ -17,7 +17,7 @@
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <libtasn1.h>
+#include <grub/libtasn1.h>
#include <grub/types.h>
#include <grub/err.h>
#include <grub/mm.h>
diff --git a/grub-core/commands/appendedsig/gnutls_asn1_tab.c b/grub-core/commands/appendedsig/gnutls_asn1_tab.c
index efc0c14..16998f2 100644
--- a/grub-core/commands/appendedsig/gnutls_asn1_tab.c
+++ b/grub-core/commands/appendedsig/gnutls_asn1_tab.c
@@ -1,5 +1,5 @@
#include <grub/mm.h>
-#include <libtasn1.h>
+#include <grub/libtasn1.h>
/*
* Imported from gnutls.asn.
diff --git a/grub-core/commands/appendedsig/pkix_asn1_tab.c b/grub-core/commands/appendedsig/pkix_asn1_tab.c
index ec5f87b..fdc989a 100644
--- a/grub-core/commands/appendedsig/pkix_asn1_tab.c
+++ b/grub-core/commands/appendedsig/pkix_asn1_tab.c
@@ -1,5 +1,5 @@
#include <grub/mm.h>
-#include <libtasn1.h>
+#include <grub/libtasn1.h>
/*
* Imported from pkix.asn.
diff --git a/grub-core/commands/appendedsig/x509.c b/grub-core/commands/appendedsig/x509.c
index bc15266..08846aa 100644
--- a/grub-core/commands/appendedsig/x509.c
+++ b/grub-core/commands/appendedsig/x509.c
@@ -17,7 +17,7 @@
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <libtasn1.h>
+#include <grub/libtasn1.h>
#include <grub/types.h>
#include <grub/err.h>
#include <grub/mm.h>

View File

@ -0,0 +1,81 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nicolas Frayer <nfrayer@redhat.com>
Date: Wed, 19 Nov 2025 14:17:51 +0100
Subject: [PATCH] docs: fix duplicated entries
Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
---
docs/grub.texi | 60 ----------------------------------------------------------
1 file changed, 60 deletions(-)
diff --git a/docs/grub.texi b/docs/grub.texi
index e4f36df..9f5eb68 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -7025,66 +7025,6 @@ GRUB will be restricted and some operations/commands cannot be executed.
The @samp{lockdown} variable is set to @samp{y} when the GRUB is locked down.
Otherwise it does not exit.
-@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.
-
-@section 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 enrol the public key used into a relevant firmware
-key database.
-
-@section Signing GRUB with an appended signature
-
-The @file{core.elf} itself can be signed with a Linux kernel module-style
-appended signature.
-
-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}.
-
-An image can be signed this way using the @command{sign-file} command from
-the Linux kernel:
-
-@example
-@group
-# grub.key is your private key and certificate.der is your public key
-
-# Determine the size of the appended signature. It depends on the signing
-# certificate and the hash algorithm
-touch empty
-sign-file SHA256 grub.key certificate.der empty empty.sig
-SIG_SIZE=`stat -c '%s' empty.sig`
-rm empty empty.sig
-
-# Build a grub image with $SIG_SIZE reserved for the signature
-grub-install --appended-signature-size $SIG_SIZE --modules="..." ...
-
-# Replace the reserved size with a signature:
-# cut off the last $SIG_SIZE bytes with truncate's minus modifier
-truncate -s -$SIG_SIZE /boot/grub/powerpc-ieee1275/core.elf core.elf.unsigned
-# sign the trimmed file with an appended signature, restoring the correct size
-sign-file SHA256 grub.key certificate.der core.elf.unsigned core.elf.signed
-
-# Don't forget to install the signed image as required
-# (e.g. on powerpc-ieee1275, to the PReP partition)
-@end group
-@end example
-
-As with UEFI secure boot, it is necessary to build in the required modules,
-or sign them separately.
-
@subsection Command line and menuentry editor protection
The TPM key protector provides full disk encryption support on servers or

View File

@ -0,0 +1,345 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Date: Mon, 6 Oct 2025 12:54:46 +0530
Subject: [PATCH] powerpc/ieee1275: Add support for signing GRUB with an
appended signature
Add infrastructure to allow firmware to verify the integrity of GRUB
by use of a Linux-kernel-module-style appended signature. We initially
target powerpc-ieee1275, but the code should be extensible to other
platforms.
Usually these signatures are appended to a file without modifying the
ELF file itself. (This is what the 'sign-file' tool does, for example.)
The verifier loads the signed file from the file system and looks at the
end of the file for the appended signature. However, on powerpc-ieee1275
platforms, the bootloader is often stored directly in the PReP partition
as raw bytes without a file-system. This makes determining the location
of an appended signature more difficult.
To address this, we add a new ELF Note.
The name field of shall be the string "Appended-Signature", zero-padded
to 4 byte alignment. The type field shall be 0x41536967 (the ASCII values
for the string "ASig"). It must be the final section in the ELF binary.
The description shall contain the appended signature structure as defined
by the Linux kernel. The description will also be padded to be a multiple
of 4 bytes. The padding shall be added before the appended signature
structure (not at the end) so that the final bytes of a signed ELF file
are the appended signature magic.
A subsequent patch documents how to create a GRUB core.img validly signed
under this scheme.
Signed-off-by: Rashmica Gupta <rashmica.g@gmail.com>
Signed-off-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
include/grub/util/install.h | 2 +-
include/grub/util/mkimage.h | 4 +--
util/grub-install-common.c | 28 ++++++++++-----------
util/grub-mkimage.c | 19 +++++++-------
util/grub-mkimagexx.c | 61 +++++++++++++++++++++++----------------------
util/mkimage.c | 20 ++++++++-------
6 files changed, 68 insertions(+), 66 deletions(-)
diff --git a/include/grub/util/install.h b/include/grub/util/install.h
index 93c1f0e..14f4489 100644
--- a/include/grub/util/install.h
+++ b/include/grub/util/install.h
@@ -70,7 +70,7 @@
{ "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}, \
+ 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, \
diff --git a/include/grub/util/mkimage.h b/include/grub/util/mkimage.h
index 881e303..244e39d 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, size_t appsig_size, 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, size_t appsig_size, 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/util/grub-install-common.c b/util/grub-install-common.c
index 41251ce..c4b5cb3 100644
--- a/util/grub-install-common.c
+++ b/util/grub-install-common.c
@@ -475,6 +475,7 @@ int
grub_install_parse (int key, char *arg)
{
const char *end;
+
switch (key)
{
case GRUB_INSTALL_OPTIONS_INSTALL_CORE_COMPRESS:
@@ -580,10 +581,11 @@ grub_install_parse (int key, char *arg)
case GRUB_INSTALL_OPTIONS_GRUB_MKIMAGE:
return 1;
case GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE:
- grub_errno = 0;
- appsig_size = grub_strtol(arg, &end, 10);
- if (grub_errno)
- return 0;
+ 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;
@@ -709,14 +711,12 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
grub_util_info ("grub-mkimage --directory '%s' --prefix '%s' --output '%s'"
" --format '%s' --compression '%s'"
- " --appended-signture-size %zu %s%s%s\n",
- " --format '%s' --compression '%s'%s%s%s%s\n",
- dir, prefix, outname,
- mkimage_target, compnames[compression],
- appsig_size,
- note ? " --note" : "",
- disable_shim_lock ? " --disable-shim-lock" : "",
- disable_cli ? " --disable-cli" : "", s);
+ " --appended-signature-size %zu %s %s %s %s\n",
+ dir, prefix, outname,
+ mkimage_target, compnames[compression], appsig_size,
+ note ? " --note" : "",
+ disable_shim_lock ? " --disable-shim-lock" : "",
+ disable_cli ? " --disable-cli" : "", s);
free (s);
tgt = grub_install_get_image_target (mkimage_target);
@@ -725,9 +725,7 @@ 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,
- x509keys, nx509keys,
- config_path, tgt,
+ pubkeys, npubkeys, x509keys, nx509keys, config_path, tgt,
note, appsig_size, compression, dtb, sbat,
disable_shim_lock, disable_cli);
while (dc--)
diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c
index 89ca81c..e3511b0 100644
--- a/util/grub-mkimage.c
+++ b/util/grub-mkimage.c
@@ -85,8 +85,8 @@ 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},
- {"verbose", 'v', 0, 0, N_("print verbose messages."), 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 }
};
@@ -133,8 +133,8 @@ struct arguments
char *sbat;
int note;
int disable_shim_lock;
- size_t appsig_size;
int disable_cli;
+ size_t appsig_size;
const struct grub_install_image_target_desc *image_target;
grub_compression_t comp;
};
@@ -179,10 +179,11 @@ argp_parser (int key, char *arg, struct argp_state *state)
break;
case 'S':
- grub_errno = 0;
- arguments->appsig_size = grub_strtol(arg, &end, 10);
- if (grub_errno)
- return 0;
+ 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':
@@ -351,9 +352,9 @@ main (int argc, char *argv[])
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.appsig_size,
+ arguments.comp, arguments.dtb,
+ arguments.sbat, arguments.disable_shim_lock,
arguments.disable_cli);
if (grub_util_file_sync (fp) < 0)
diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c
index b993054..d185246 100644
--- a/util/grub-mkimagexx.c
+++ b/util/grub-mkimagexx.c
@@ -85,15 +85,6 @@ struct grub_ieee1275_note
struct grub_ieee1275_note_desc descriptor;
};
-#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)];
-};
-
#define GRUB_XEN_NOTE_NAME "Xen"
struct fixup_block_list
@@ -124,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)
{
@@ -225,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, size_t appsig_size, 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)
{
@@ -249,7 +248,7 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc
if (appsig_size)
{
phnum++;
- footer_size += ALIGN_UP(sizeof (struct grub_appended_signature_note) + appsig_size, 4);
+ footer_size += ALIGN_UP (sizeof (struct grub_appended_signature_note), 4);
}
if (image_target->id != IMAGE_LOONGSON_ELF)
@@ -542,29 +541,31 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc
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 *)
- (elf_img + program_size + header_size + (note ? sizeof (struct grub_ieee1275_note) : 0));
+ 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);
+ 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);
- 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 + (note ? sizeof (struct grub_ieee1275_note) : 0));
- }
+ 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);
+ }
{
char *str_start = (elf_img + sizeof (*ehdr) + phnum * sizeof (*phdr)
diff --git a/util/mkimage.c b/util/mkimage.c
index cc4c79d..15cfd4b 100644
--- a/util/mkimage.c
+++ b/util/mkimage.c
@@ -883,12 +883,11 @@ 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 **x509key_paths,
- size_t nx509keys, 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, size_t appsig_size, grub_compression_t comp,
- const char *dtb_path, const char *sbat_path,
- int disable_shim_lock, int disable_cli)
+ 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)
{
char *kernel_img, *core_img;
size_t total_module_size, core_size;
@@ -978,6 +977,9 @@ 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"));
+ 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);
@@ -1884,11 +1886,11 @@ 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, appsig_size, sbat, &core_img,
- &core_size, target_addr, &layout);
+ 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, appsig_size, sbat, &core_img,
- &core_size, target_addr, &layout);
+ grub_mkimage_generate_elf64 (image_target, note, sbat, appsig_size, &core_img, &core_size,
+ target_addr, &layout);
}
break;
}

View File

@ -476,7 +476,6 @@ fi \
--appended-signature-size ${APPENDED_SIG_SIZE} \\\ --appended-signature-size ${APPENDED_SIG_SIZE} \\\
${GRUB_MODULES} \ ${GRUB_MODULES} \
if [ -x /usr/bin/rpm-sign ]; then \ if [ -x /usr/bin/rpm-sign ]; then \
truncate -s -${APPENDED_SIG_SIZE} %{2}.orig \
rpm-sign --key %{4} \\\ rpm-sign --key %{4} \\\
--lkmsign %{2}.orig \\\ --lkmsign %{2}.orig \\\
--output %{2} \ --output %{2} \

View File

@ -370,3 +370,39 @@ Patch0370: 0370-Set-correctly-the-memory-attributes-for-the-kernel-P.patch
Patch0371: 0371-script-execute-Don-t-let-trailing-blank-lines-determ.patch Patch0371: 0371-script-execute-Don-t-let-trailing-blank-lines-determ.patch
Patch0372: 0372-normal-menu-Check-return-code-of-the-script-when-exe.patch Patch0372: 0372-normal-menu-Check-return-code-of-the-script-when-exe.patch
Patch0373: 0373-Include-license-into-grub-set-password-util.patch Patch0373: 0373-Include-license-into-grub-set-password-util.patch
Patch0374: 0374-libgcrypt-Import-libgcrypt-1.11.patch
Patch0375: 0375-b64dec-Import-b64dec-from-gpg-error.patch
Patch0376: 0376-b64dec-Add-harness-for-compilation-in-GRUB-environme.patch
Patch0377: 0377-libgcrypt-Adjust-import-script-definitions-and-API-u.patch
Patch0378: 0378-tests-Add-DSA-and-RSA-SEXP-tests.patch
Patch0379: 0379-keccak-Disable-acceleration-with-SSE-asm.patch
Patch0380: 0380-libgcrypt-Fix-Coverity-warnings.patch
Patch0381: 0381-libgcrypt-Remove-now-unneeded-compilation-flag.patch
Patch0382: 0382-libgcrypt-Ignore-sign-compare-warnings.patch
Patch0383: 0383-libgcrypt-Import-blake-family-of-hashes.patch
Patch0384: 0384-util-import_gcry-Make-compatible-with-Python-3.4.patch
Patch0385: 0385-util-import_gcry-Fix-pylint-warnings.patch
Patch0386: 0386-libgcrypt-Don-t-use-64-bit-division-on-platforms-whe.patch
Patch0387: 0387-libgcrypt-Fix-a-memory-leak.patch
Patch0388: 0388-docs-Write-how-to-import-new-libgcrypt.patch
Patch0389: 0389-pgp-Rename-OBJ_TYPE_PUBKEY-to-OBJ_TYPE_GPG_PUBKEY.patch
Patch0390: 0390-grub-install-Support-embedding-x509-certificates.patch
Patch0391: 0391-appended-signatures-Import-GNUTLS-s-ASN.1-descriptio.patch
Patch0392: 0392-appended-signatures-Parse-ASN1-node.patch
Patch0393: 0393-appended-signatures-Parse-PKCS-7-signed-data.patch
Patch0394: 0394-appended-signatures-Parse-X.509-certificates.patch
Patch0395: 0395-powerpc-ieee1275-Enter-lockdown-based-on-ibm-secure-.patch
Patch0396: 0396-appended-signatures-Support-verifying-appended-signa.patch
Patch0397: 0397-powerpc-ieee1275-Read-the-db-and-dbx-secure-boot-var.patch
Patch0398: 0398-appended-signatures-Introducing-key-management-envir.patch
Patch0399: 0399-appended-signatures-Create-db-and-dbx-lists.patch
Patch0400: 0400-appended-signatures-Using-db-and-dbx-lists-for-signa.patch
Patch0401: 0401-appended-signatures-GRUB-commands-to-manage-the-cert.patch
Patch0402: 0402-appended-signatures-GRUB-commands-to-manage-the-hash.patch
Patch0403: 0403-appended-signatures-Verification-tests.patch
Patch0404: 0404-docs-grub-Document-signing-GRUB-under-UEFI.patch
Patch0405: 0405-docs-grub-Document-signing-GRUB-with-an-appended-sig.patch
Patch0406: 0406-docs-grub-Document-appended-signature.patch
Patch0407: 0407-libtasn1-Fix-include-path-for-grub.patch
Patch0408: 0408-docs-fix-duplicated-entries.patch
Patch0409: 0409-powerpc-ieee1275-Add-support-for-signing-GRUB-with-a.patch

View File

@ -17,7 +17,7 @@
Name: grub2 Name: grub2
Epoch: 1 Epoch: 1
Version: 2.12 Version: 2.12
Release: 33%{?dist} Release: 34%{?dist}
Summary: Bootloader with support for Linux, Multiboot and more Summary: Bootloader with support for Linux, Multiboot and more
License: GPL-3.0-or-later License: GPL-3.0-or-later
URL: http://www.gnu.org/software/grub/ URL: http://www.gnu.org/software/grub/
@ -574,6 +574,10 @@ fi
%endif %endif
%changelog %changelog
* Fri Nov 21 2025 Nicolas Frayer <nfrayer@redhat.com> 2.12-34
- powerpc: Add appended signature feature
- Resolves: #RHEL-24510
* Thu Nov 06 2025 Leo Sandoval <lsandova@redhat.com> 2.12-33 * Thu Nov 06 2025 Leo Sandoval <lsandova@redhat.com> 2.12-33
- Include license into grub-set-password util - Include license into grub-set-password util
- Resolves: #RHEL-120704 - Resolves: #RHEL-120704