Skip to content

Commit 8d28c54

Browse files
committed
add sbsiglist tool
Signed-off-by: Stephan Mueller <smueller@chronox.de>
1 parent 65bae5b commit 8d28c54

File tree

8 files changed

+479
-2
lines changed

8 files changed

+479
-2
lines changed

CHANGES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Changes 1.7.0-prerelease
3737

3838
* Add support of PEM
3939

40-
* Add sbsign, sbverify tool as a drop-in replacement for the one provided with http://git.kernel.org/pub/scm/linux/kernel/git/jejb/sbsigntools.git
40+
* Add sbsign, sbverify, sbsiglist, sbvarsign tools as a drop-in replacement for the one provided with http://git.kernel.org/pub/scm/linux/kernel/git/jejb/sbsigntools.git
4141

4242
* PKCS7 generator: support altering the generation process (allow content type setting, additional authenticated attributes)
4343

LICENSE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,3 +610,4 @@ apps/src/coff/pe.h
610610
apps/src/coff/external.h
611611
apps/src/efivars.h
612612
apps/src/sbvarsign.c
613+
apps/src/sbsiglist.c

apps/src/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ The `lc_pkcs7_generator` is the command line application providing a PKCS#7 / CM
3232

3333
## Secure Boot Signing Tools Supporting PQC
3434

35-
The `sbsign`, `sbverify`, `sbvarsign` tools are drop-in replacements for the respective tools provided with http://git.kernel.org/pub/scm/linux/kernel/git/jejb/sbsigntools.git. They provide the same behavior and the same command line options as the originals. The key difference is that the created signatures are PQC signatures as supported by leancrypto:
35+
The `sbsign`, `sbverify`, `sbvarsign`, `sbsiglist` tools are drop-in replacements for the respective tools provided with http://git.kernel.org/pub/scm/linux/kernel/git/jejb/sbsigntools.git. They provide the same behavior and the same command line options as the originals. The key difference is that the created signatures are PQC signatures as supported by leancrypto:
3636

3737
* ML-DSA44, ML-DSA65, ML-DSA87
3838

apps/src/meson.build

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,20 @@ if (host_machine.system() != 'windows' and
180180
install: true,
181181
install_dir: get_option('libexecdir') / meson.project_name()
182182
)
183+
184+
sbsiglist_files = internal_src
185+
sbsiglist_files += files([
186+
'lc_x509_generator_file_helper.c',
187+
'sbsiglist.c',
188+
])
189+
190+
sbsiglist = executable('sbsiglist',
191+
[ sbsiglist_files ],
192+
include_directories: [ include_internal_dirs ],
193+
dependencies: leancrypto,
194+
install: true,
195+
install_dir: get_option('libexecdir') / meson.project_name()
196+
)
183197
endif
184198

185199
endif

apps/src/sbsiglist.c

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
/*
2+
* Copyright (C) 2026, Stephan Mueller <smueller@chronox.de>
3+
*
4+
* License: see LICENSE file in root directory
5+
*
6+
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
7+
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
8+
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
9+
* WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
10+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
11+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
12+
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
13+
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
14+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
15+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
16+
* USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
17+
* DAMAGE.
18+
*/
19+
/*
20+
* This implementation is intended to provide a drop-in replacement for the
21+
* sbsign tool from
22+
* http://git.kernel.org/pub/scm/linux/kernel/git/jejb/sbsigntools.git.
23+
*
24+
* The file is derived from this code with the following license:
25+
*/
26+
/*
27+
* Copyright (C) 2012 Jeremy Kerr <jeremy.kerr@canonical.com>
28+
*
29+
* This program is free software; you can redistribute it and/or
30+
* modify it under the terms of the GNU General Public License
31+
* as published by the Free Software Foundation; either version 3
32+
* of the License, or (at your option) any later version.
33+
*
34+
* This program is distributed in the hope that it will be useful,
35+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
36+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37+
* GNU General Public License for more details.
38+
*
39+
* You should have received a copy of the GNU General Public License
40+
* along with this program; if not, write to the Free Software
41+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
42+
* USA.
43+
*
44+
* In addition, as a special exception, the copyright holders give
45+
* permission to link the code of portions of this program with the OpenSSL
46+
* library under certain conditions as described in each individual source file,
47+
* and distribute linked combinations including the two.
48+
*
49+
* You must obey the GNU General Public License in all respects for all
50+
* of the code used other than OpenSSL. If you modify file(s) with this
51+
* exception, you may extend this exception to your version of the
52+
* file(s), but you are not obligated to do so. If you do not wish to do
53+
* so, delete this exception statement from your version. If you delete
54+
* this exception statement from all source files in the program, then
55+
* also delete it here.
56+
*/
57+
#define _GNU_SOURCE
58+
59+
#include <fcntl.h>
60+
#include <stdio.h>
61+
#include <stdlib.h>
62+
#include <stdint.h>
63+
#include <unistd.h>
64+
#include <string.h>
65+
#include <sys/stat.h>
66+
#include <sys/types.h>
67+
68+
#include <getopt.h>
69+
70+
#include "efivars.h"
71+
72+
#include "helper.h"
73+
#include "lc_memory_support.h"
74+
#include "lc_status.h"
75+
#include "lc_uuid.h"
76+
#include "lc_x509_generator_file_helper.h"
77+
#include "ret_checkers.h"
78+
79+
static const char *toolname = "sbsiglist";
80+
81+
static struct option options[] = {
82+
{ "output", required_argument, NULL, 'o' },
83+
{ "type", required_argument, NULL, 't' },
84+
{ "owner", required_argument, NULL, 'w' },
85+
{ "verbose", no_argument, NULL, 'v' },
86+
{ "help", no_argument, NULL, 'h' },
87+
{ "version", no_argument, NULL, 'V' },
88+
{ NULL, 0, NULL, 0 },
89+
};
90+
91+
struct cert_type {
92+
const char *name;
93+
const EFI_GUID guid;
94+
unsigned int sigsize;
95+
};
96+
97+
struct cert_type cert_types[] = {
98+
{ "x509", EFI_CERT_X509_GUID, 0 },
99+
{ "sha256", EFI_CERT_SHA256_GUID, 32 },
100+
};
101+
102+
struct siglist_context {
103+
int verbose;
104+
105+
const char *infilename;
106+
char *outfilename;
107+
const struct cert_type *type;
108+
EFI_GUID owner;
109+
110+
uint8_t *data;
111+
size_t data_len;
112+
113+
EFI_SIGNATURE_LIST *siglist;
114+
};
115+
116+
117+
static void usage(void)
118+
{
119+
unsigned int i;
120+
121+
printf("Usage: %s [options] --owner <guid> --type <type> <sig-file>\n"
122+
"Create an EFI_SIGNATURE_LIST from a signature file\n"
123+
"Options:\n"
124+
"\t--owner <guid> Signature owner GUID\n"
125+
"\t--type <type> Signature type. One of:\n",
126+
toolname);
127+
128+
for (i = 0; i < ARRAY_SIZE(cert_types); i++)
129+
printf("\t %s\n", cert_types[i].name);
130+
131+
printf("\t--output <file> write signed data to <file>\n"
132+
"\t (default <sig-file>.siglist)\n");
133+
}
134+
135+
static void version(void)
136+
{
137+
char version[500];
138+
139+
memset(version, 0, sizeof(version));
140+
lc_status(version, sizeof(version));
141+
142+
fprintf(stderr, "Leancrypto %s\n", toolname);
143+
fprintf(stderr, "%s\n", version);
144+
}
145+
146+
static int siglist_create(struct siglist_context *ctx)
147+
{
148+
EFI_SIGNATURE_LIST *siglist;
149+
EFI_SIGNATURE_DATA *sigdata;
150+
uint32_t size;
151+
int ret = 0;
152+
153+
if (ctx->type->sigsize && ctx->data_len != ctx->type->sigsize) {
154+
fprintf(stderr, "Error: signature lists of type '%s' expect "
155+
"%d bytes of data, "
156+
"%zd bytes provided.\n",
157+
ctx->type->name,
158+
ctx->type->sigsize,
159+
ctx->data_len);
160+
return -EINVAL;
161+
}
162+
163+
size = (uint32_t)(sizeof(*siglist) + sizeof(*sigdata) + ctx->data_len);
164+
165+
siglist = calloc(1, size);
166+
CKNULL(siglist, -ENOMEM);
167+
sigdata = (void *)(siglist + 1);
168+
169+
siglist->SignatureType = ctx->type->guid;
170+
siglist->SignatureListSize = size;
171+
siglist->SignatureHeaderSize = 0;
172+
siglist->SignatureSize = (uint32_t)(ctx->data_len + sizeof(*sigdata));
173+
174+
sigdata->SignatureOwner = ctx->owner;
175+
176+
memcpy(sigdata->SignatureData, ctx->data, ctx->data_len);
177+
178+
ctx->siglist = siglist;
179+
180+
out:
181+
return ret;
182+
}
183+
184+
static int parse_guid(const char *str, EFI_GUID *guid)
185+
{
186+
uint8_t uuid[16];
187+
int ret;
188+
189+
CKINT(lc_uuid_hex2bin(str, strlen(str), uuid));
190+
191+
/* convert to an EFI_GUID */
192+
guid->Data1 = (UINT32)(uuid[0] << 24 | uuid[1] << 16 | uuid[2] << 8 |
193+
uuid[3]);
194+
guid->Data2 = (UINT16)(uuid[4] << 8 | uuid[5]);
195+
guid->Data3 = (UINT16)(uuid[6] << 8 | uuid[7]);
196+
memcpy(guid->Data4, &uuid[8], sizeof(guid->Data4));
197+
198+
out:
199+
return ret;
200+
}
201+
202+
static struct cert_type *parse_type(const char *str)
203+
{
204+
unsigned int i;
205+
206+
for (i = 0; i < ARRAY_SIZE(cert_types); i++)
207+
if (!strcasecmp(cert_types[i].name, str))
208+
return &cert_types[i];
209+
210+
return NULL;
211+
}
212+
213+
static int set_default_outfilename(struct siglist_context *ctx)
214+
{
215+
static const char *extension = "siglist";
216+
size_t len = strlen(ctx->infilename) + 1 + 7 + 1;
217+
int ret;
218+
219+
CKINT(lc_alloc_aligned((void **)&ctx->outfilename, sizeof(uint64_t),
220+
len));
221+
snprintf(ctx->outfilename, len, "%s.%s", ctx->infilename, extension);
222+
223+
out:
224+
return ret;
225+
}
226+
227+
int main(int argc, char **argv)
228+
{
229+
const char *type_str, *owner_guid_str;
230+
struct siglist_context ctx = { 0 };
231+
int c, ret;
232+
233+
owner_guid_str = NULL;
234+
type_str = NULL;
235+
236+
for (;;) {
237+
int idx;
238+
c = getopt_long(argc, argv, "o:t:w:ivVh", options, &idx);
239+
if (c == -1)
240+
break;
241+
242+
switch (c) {
243+
case 'o':
244+
ctx.outfilename = optarg;
245+
break;
246+
case 't':
247+
type_str = optarg;
248+
break;
249+
case 'w':
250+
owner_guid_str = optarg;
251+
break;
252+
case 'v':
253+
ctx.verbose = 1;
254+
break;
255+
case 'V':
256+
version();
257+
ret = 0;
258+
goto out;
259+
case 'h':
260+
usage();
261+
ret = 0;
262+
goto out;
263+
}
264+
}
265+
266+
if (argc != optind + 1) {
267+
usage();
268+
ret = -EINVAL;
269+
goto out;
270+
}
271+
272+
ctx.infilename = argv[optind];
273+
274+
CKNULL_LOG(type_str, -EINVAL, "No type specified\n");
275+
CKNULL_LOG(owner_guid_str, -EINVAL, "No owner specified\n");
276+
277+
ctx.type = parse_type(type_str);
278+
CKNULL_LOG(ctx.type, -EINVAL, "Invalid type '%s'\n", type_str);
279+
280+
CKINT_LOG(parse_guid(owner_guid_str, &ctx.owner),
281+
"Invalid owner GUID '%s'\n", owner_guid_str);
282+
283+
if (!ctx.outfilename)
284+
CKINT(set_default_outfilename(&ctx));
285+
286+
CKINT_LOG(get_data(ctx.infilename, &ctx.data, &ctx.data_len,
287+
lc_pem_flag_nopem), "Can't read input file %s\n",
288+
ctx.infilename);
289+
290+
CKINT((siglist_create(&ctx)));
291+
292+
CKINT_LOG(write_data(ctx.outfilename, (uint8_t *)ctx.siglist,
293+
ctx.siglist->SignatureListSize, lc_pem_flag_nopem),
294+
"Can't write output file %s\n", ctx.outfilename);
295+
296+
out:
297+
if (ctx.siglist)
298+
free(ctx.siglist);
299+
if (ctx.outfilename)
300+
lc_free(ctx.outfilename);
301+
release_data(ctx.data, ctx.data_len, lc_pem_flag_nopem);
302+
return -ret;
303+
}

apps/tests/meson.build

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ hasher_test_script = find_program('hasher-test.sh', required: true)
22
siggen_test_script = find_program('siggen_tester.sh', required: true)
33
sbsign_test_script = find_program('sbsign_test.sh', required: true)
44
sbvarsign_test_script = find_program('sbvarsign_test.sh', required: true)
5+
sbsiglist_test_script = find_program('sbsiglist_test.sh', required: true)
56

67
# Application can be re-generated as outlined in README.md
78
# "Library Build for EFI Environment"
@@ -911,5 +912,15 @@ if (host_machine.system() != 'windows' and
911912
],
912913
timeout: 300, suite: regression,
913914
should_fail: fips140_negative_expect_fail)
915+
916+
if get_option('sha2-256').enabled()
917+
test('Secure boot sbsiglist tool',
918+
sbsiglist_test_script,
919+
args: [ sbsiglist.full_path(),
920+
sha256sum.full_path()
921+
],
922+
timeout: 300, suite: regression,
923+
should_fail: fips140_negative_expect_fail)
924+
endif
914925
endif
915926
endif

0 commit comments

Comments
 (0)