futility: Add --keyset option to sign command for BIOS and kernel

This patch adds --keyset option for sign command for BIOS_IMAGE,
RAW_FIRMWARE, RAW_KERNEL and KERN_PREAMBLE file types. The default value
of this option is '/usr/share/vboot/devkeys'. It allows futility to load
public and private keys, and keyblocks from under this path, when they
were not provided manually using their respective options.

Files loaded by default for BIOS_IMAGE and RAW_FIRMWARE:
- ${keysetdir}/firmware_data_key.vbprivk
- ${keysetdir}/firmware.keyblock
- ${keysetdir}/kernel_subkey.vbpubk

Files loaded by default for RAW_KERNEL:
- ${keysetdir}/kernel_data_key.vbprivk
- ${keysetdir}/kernel.keyblock

File loaded by default for KERN_PREAMBLE:
- ${keysetdir}/kernel_data_key.vbprivk

BUG=none
BRANCH=none
TEST=make runfutiltests

Signed-off-by: Jakub Czapiga <jacz@semihalf.com>
Change-Id: Ic4026d501d88e0de7d2c6f52c7494c639d08bd15
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/3740601
Auto-Submit: Jakub Czapiga <czapiga@google.com>
Reviewed-by: Julius Werner <jwerner@chromium.org>
Commit-Queue: Julius Werner <jwerner@chromium.org>
Tested-by: Jakub Czapiga <czapiga@google.com>
diff --git a/futility/cmd_sign.c b/futility/cmd_sign.c
index b35712a..2f9a017 100644
--- a/futility/cmd_sign.c
+++ b/futility/cmd_sign.c
@@ -29,8 +29,11 @@
 #include "util_misc.h"
 #include "vb1_helper.h"
 
+#define DEFAULT_KEYSETDIR "/usr/share/vboot/devkeys"
+
 /* Options */
 struct sign_option_s sign_option = {
+	.keysetdir = DEFAULT_KEYSETDIR,
 	.version = 1,
 	.arch = ARCH_UNSPECIFIED,
 	.kloadaddr = CROS_32BIT_ENTRY_ADDR,
@@ -306,6 +309,86 @@
 	return rv;
 }
 
+static int load_keyset(void)
+{
+	char *buf = NULL;
+	int errorcnt = 0;
+	const char *s = NULL;
+	const char *b = NULL;
+	const char *k = NULL;
+	const char *format;
+	struct stat sb;
+
+	if (!sign_option.keysetdir)
+		FATAL("Keyset should never be NULL. Aborting\n");
+
+	/* Failure means this is not a directory */
+	if (stat(sign_option.keysetdir, &sb) == -1 ||
+	    (sb.st_mode & S_IFMT) != S_IFDIR)
+		format = "%s%s.%s";
+	else
+		format = "%s/%s.%s";
+
+	switch (sign_option.type) {
+	case FILE_TYPE_BIOS_IMAGE:
+	case FILE_TYPE_RAW_FIRMWARE:
+		s = "firmware_data_key";
+		b = "firmware";
+		k = "kernel_subkey";
+		break;
+	case FILE_TYPE_RAW_KERNEL:
+		s = "kernel_data_key";
+		b = "kernel";
+		break;
+	case FILE_TYPE_KERN_PREAMBLE:
+		s = "kernel_data_key";
+		break;
+	default:
+		return 0;
+	}
+
+	if (s && !sign_option.signprivate) {
+		if (asprintf(&buf, format, sign_option.keysetdir, s,
+			     "vbprivk") <= 0)
+			FATAL("Failed to allocate string\n");
+		INFO("Loading private data key from default keyset: %s\n", buf);
+		sign_option.signprivate = vb2_read_private_key(buf);
+		if (!sign_option.signprivate) {
+			ERROR("Error reading %s\n", buf);
+			errorcnt++;
+		}
+		free(buf);
+	}
+
+	if (b && !sign_option.keyblock) {
+		if (asprintf(&buf, format, sign_option.keysetdir, b,
+			     "keyblock") <= 0)
+			FATAL("Failed to allocate string\n");
+		INFO("Loading keyblock from default keyset: %s\n", buf);
+		sign_option.keyblock = vb2_read_keyblock(buf);
+		if (!sign_option.keyblock) {
+			ERROR("Error reading %s\n", buf);
+			errorcnt++;
+		}
+		free(buf);
+	}
+
+	if (k && !sign_option.kernel_subkey) {
+		if (asprintf(&buf, format, sign_option.keysetdir, k,
+			     "vbpubk") <= 0)
+			FATAL("Failed to allocate string\n");
+		INFO("Loading kernel subkey from default keyset: %s\n", buf);
+		sign_option.kernel_subkey = vb2_read_packed_key(buf);
+		if (!sign_option.kernel_subkey) {
+			ERROR("Error reading %s\n", buf);
+			errorcnt++;
+		}
+		free(buf);
+	}
+
+	return errorcnt;
+}
+
 static const char usage_pubkey[] = "\n"
 	"To sign a public key / create a new keyblock:\n"
 	"\n"
@@ -338,36 +421,46 @@
 	"To sign a raw firmware blob (FW_MAIN_A/B):\n"
 	"\n"
 	"Required PARAMS:\n"
-	"  -s|--signprivate FILE.vbprivk    The private firmware data key\n"
-	"  -b|--keyblock    FILE.keyblock   The keyblock containing the\n"
-	"                                     public firmware data key\n"
-	"  -k|--kernelkey   FILE.vbpubk     The public kernel subkey\n"
 	"  -v|--version     NUM             The firmware version number\n"
 	"  [--fv]           INFILE"
 	"          The raw firmware blob (FW_MAIN_A/B)\n"
 	"  [--outfile]      OUTFILE         Output VBLOCK_A/B\n"
 	"\n"
 	"Optional PARAMS:\n"
+	"  -s|--signprivate FILE.vbprivk    The private firmware data key\n"
+	"  -b|--keyblock    FILE.keyblock   The keyblock containing the\n"
+	"                                     public firmware data key\n"
+	"  -k|--kernelkey   FILE.vbpubk     The public kernel subkey\n"
 	"  -f|--flags       NUM             The preamble flags value"
 	" (default is 0)\n"
+	"  -K|--keyset      PATH            Prefix of private firmware data"
+	" key,\n"
+	"                                   keyblock and public kernel"
+	" subkey.\n"
+	"                                   Prefix must be valid path with\n"
+	"                                   optional file name prefix.\n"
+	"                                   Used as defaults for -s, -b and"
+	" -k,\n"
+	"                                   if not passed expliticly\n"
+	"                                   (default is '%s')\n"
 	"\n";
 static void print_help_raw_firmware(int argc, char *argv[])
 {
-	puts(usage_fw_main);
+	printf(usage_fw_main, DEFAULT_KEYSETDIR);
 }
 
 static const char usage_bios[] = "\n"
 	"To sign a complete firmware image (bios.bin):\n"
 	"\n"
 	"Required PARAMS:\n"
-	"  -s|--signprivate FILE.vbprivk    The private firmware data key\n"
-	"  -b|--keyblock    FILE.keyblock   The keyblock containing the\n"
-	"                                     public firmware data key\n"
-	"  -k|--kernelkey   FILE.vbpubk     The public kernel subkey\n"
 	"  [--infile]       INFILE          Input firmware image (modified\n"
 	"                                     in place if no OUTFILE given)\n"
 	"\n"
 	"Optional PARAMS:\n"
+	"  -s|--signprivate FILE.vbprivk    The private firmware data key\n"
+	"  -b|--keyblock    FILE.keyblock   The keyblock containing the\n"
+	"                                     public firmware data key\n"
+	"  -k|--kernelkey   FILE.vbpubk     The public kernel subkey\n"
 	"  -v|--version     NUM             The firmware version number"
 	" (default %d)\n"
 	"  -f|--flags       NUM             The preamble flags value"
@@ -375,21 +468,27 @@
 	"                                     unchanged, or 0 if unknown)\n"
 	"  -d|--loemdir     DIR             Local OEM output vblock directory\n"
 	"  -l|--loemid      STRING          Local OEM vblock suffix\n"
+	"  -K|--keyset      PATH            Prefix of private firmware data"
+	" key,\n"
+	"                                   keyblock and public kernel"
+	" subkey.\n"
+	"                                   Prefix must be valid path with\n"
+	"                                   optional file name prefix.\n"
+	"                                   Used as defaults for -s, -b and"
+	" -k,\n"
+	"                                   if not passed expliticly\n"
+	"                                   (default is '%s')\n"
 	"  [--outfile]      OUTFILE         Output firmware image\n"
 	"\n";
 static void print_help_bios_image(int argc, char *argv[])
 {
-	printf(usage_bios, sign_option.version);
+	printf(usage_bios, sign_option.version, DEFAULT_KEYSETDIR);
 }
 
 static const char usage_new_kpart[] = "\n"
 	"To create a new kernel partition image (/dev/sda2, /dev/mmcblk0p2):\n"
 	"\n"
 	"Required PARAMS:\n"
-	"  -s|--signprivate FILE.vbprivk"
-	"    The private key to sign the kernel blob\n"
-	"  -b|--keyblock    FILE.keyblock   Keyblock containing the public\n"
-	"                                     key to verify the kernel blob\n"
 	"  -v|--version     NUM             The kernel version number\n"
 	"  --bootloader     FILE            Bootloader stub\n"
 	"  --config         FILE            The kernel commandline file\n"
@@ -399,6 +498,10 @@
 	"  [--outfile]      OUTFILE         Output kernel partition or vblock\n"
 	"\n"
 	"Optional PARAMS:\n"
+	"  -s|--signprivate FILE.vbprivk"
+	"    The private key to sign the kernel blob\n"
+	"  -b|--keyblock    FILE.keyblock   Keyblock containing the public\n"
+	"                                     key to verify the kernel blob\n"
 	"  --kloadaddr      NUM"
 	"             RAM address to load the kernel body\n"
 	"                                     (default %#x)\n"
@@ -407,22 +510,33 @@
 	" --vblockonly                      Emit just the vblock (requires a\n"
 	"                                     distinct outfile)\n"
 	"  -f|--flags       NUM             The preamble flags value\n"
+	"  -K|--keyset      DIR             Path to directory containing"
+	" private\n"
+	"                                   kernel data key, and keyblock\n"
+	"  -K|--keyset      PATH            Prefix of private kernel data key\n"
+	"                                   and keyblock.\n"
+	"                                   Prefix must be valid path with\n"
+	"                                   optional file name prefix.\n"
+	"                                   Used as defaults for -s and -b,\n"
+	"                                   if not passed expliticly\n"
+	"                                   (default is '%s')\n"
 	"\n";
 static void print_help_raw_kernel(int argc, char *argv[])
 {
-	printf(usage_new_kpart, sign_option.kloadaddr, sign_option.padding);
+	printf(usage_new_kpart, sign_option.kloadaddr, sign_option.padding,
+	       DEFAULT_KEYSETDIR);
 }
 
 static const char usage_old_kpart[] = "\n"
 	"To resign an existing kernel partition (/dev/sda2, /dev/mmcblk0p2):\n"
 	"\n"
 	"Required PARAMS:\n"
-	"  -s|--signprivate FILE.vbprivk"
-	"    The private key to sign the kernel blob\n"
 	"  [--infile]       INFILE          Input kernel partition (modified\n"
 	"                                     in place if no OUTFILE given)\n"
 	"\n"
 	"Optional PARAMS:\n"
+	"  -s|--signprivate FILE.vbprivk"
+	"    The private key to sign the kernel blob\n"
 	"  -b|--keyblock    FILE.keyblock   Keyblock containing the public\n"
 	"                                     key to verify the kernel blob\n"
 	"  -v|--version     NUM             The kernel version number\n"
@@ -433,10 +547,17 @@
 	"  --vblockonly                     Emit just the vblock (requires a\n"
 	"                                     distinct OUTFILE)\n"
 	"  -f|--flags       NUM             The preamble flags value\n"
+	"  -K|--keyset      PATH            Prefix of private kernel data"
+	" key.\n"
+	"                                   Prefix must be valid path with\n"
+	"                                   optional file name prefix.\n"
+	"                                   Used as default for -s,\n"
+	"                                   if not passed expliticly\n"
+	"                                   (default is '%s')\n"
 	"\n";
 static void print_help_kern_preamble(int argc, char *argv[])
 {
-	printf(usage_old_kpart, sign_option.padding);
+	printf(usage_old_kpart, sign_option.padding, DEFAULT_KEYSETDIR);
 }
 
 static void print_help_usbpd1(int argc, char *argv[])
@@ -619,6 +740,7 @@
 	{"flags",        1, NULL, 'f'},
 	{"loemdir",      1, NULL, 'd'},
 	{"loemid",       1, NULL, 'l'},
+	{"keyset",       1, NULL, 'K'},
 	{"fv",           1, NULL, OPT_FV},
 	{"infile",       1, NULL, OPT_INFILE},
 	{"datapubkey",   1, NULL, OPT_INFILE},	/* alias */
@@ -647,7 +769,7 @@
 	{"help",         0, NULL, OPT_HELP},
 	{NULL,           0, NULL, 0},
 };
-static const char *short_opts = ":s:b:k:S:B:v:f:d:l:";
+static const char *short_opts = ":s:b:k:v:f:d:l:K:";
 
 /* Return zero on success */
 static int parse_number_opt(const char *arg, const char *name, uint32_t *dest)
@@ -717,6 +839,9 @@
 		case 'l':
 			sign_option.loemid = optarg;
 			break;
+		case 'K':
+			sign_option.keysetdir = optarg;
+			break;
 		case OPT_FV:
 			sign_option.fv_specified = 1;
 			VBOOT_FALLTHROUGH;
@@ -908,6 +1033,10 @@
 			sign_option.type = FILE_TYPE_RAW_FIRMWARE;
 	}
 
+	/* Load keys and keyblocks from keyset path, if they were not provided
+	   earlier. */
+	errorcnt += load_keyset();
+
 	VB2_DEBUG("type=%s\n", futil_file_type_name(sign_option.type));
 
 	/* Check the arguments for the type of thing we want to sign */
diff --git a/futility/futility_options.h b/futility/futility_options.h
index da83958..32f240a 100644
--- a/futility/futility_options.h
+++ b/futility/futility_options.h
@@ -34,6 +34,7 @@
 	struct vb2_private_key *signprivate;
 	struct vb2_keyblock *keyblock;
 	struct vb2_packed_key *kernel_subkey;
+	const char *keysetdir;
 	uint32_t version;
 	int version_specified;
 	uint32_t flags;
diff --git a/tests/futility/test_sign_firmware.sh b/tests/futility/test_sign_firmware.sh
index c373803..fe8462c 100755
--- a/tests/futility/test_sign_firmware.sh
+++ b/tests/futility/test_sign_firmware.sh
@@ -111,9 +111,7 @@
   mkdir -p "${loemdir}"
 
   "${FUTILITY}" sign \
-    -s "${KEYDIR}/firmware_data_key.vbprivk" \
-    -b "${KEYDIR}/firmware.keyblock" \
-    -k "${KEYDIR}/kernel_subkey.vbpubk" \
+    -K "${KEYDIR}" \
     -v 14 \
     -f 8 \
     -d "${loemdir}" \
@@ -193,8 +191,7 @@
 
 "${FUTILITY}" sign \
   -s "${KEYDIR}/firmware_data_key.vbprivk" \
-  -b "${KEYDIR}/firmware.keyblock" \
-  -k "${KEYDIR}/kernel_subkey.vbpubk" \
+  -K "${KEYDIR}" \
   "${GOOD_CBFS_OUT}.1"
 
 "${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \
@@ -222,9 +219,8 @@
 echo -n "${count} " 1>&3
 
 "${FUTILITY}" sign \
-  -s "${KEYDIR}/firmware_data_key.vbprivk" \
   -b "${KEYDIR}/firmware.keyblock" \
-  -k "${KEYDIR}/kernel_subkey.vbpubk" \
+  -K "${KEYDIR}" \
   "${MORE_OUT}" "${MORE_OUT}.2"
 
 m=$("${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \
@@ -238,9 +234,8 @@
 
 "${FUTILITY}" load_fmap "${MORE_OUT}" VBLOCK_A:/dev/urandom VBLOCK_B:/dev/zero
 "${FUTILITY}" sign \
-  -s "${KEYDIR}/firmware_data_key.vbprivk" \
-  -b "${KEYDIR}/firmware.keyblock" \
   -k "${KEYDIR}/kernel_subkey.vbpubk" \
+  -K "${KEYDIR}" \
   "${MORE_OUT}" "${MORE_OUT}.3"
 
 m=$("${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \
@@ -256,7 +251,7 @@
 "${FUTILITY}" sign \
   -s "${KEYDIR}/firmware_data_key.vbprivk" \
   -b "${KEYDIR}/firmware.keyblock" \
-  -k "${KEYDIR}/kernel_subkey.vbpubk" \
+  -K "${KEYDIR}" \
   "${CLEAN_B}" "${CLEAN_B}.1"
 
 "${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" "${CLEAN_B}.1" \
@@ -291,8 +286,8 @@
 
 "${FUTILITY}" sign \
   -s "${KEYDIR}/firmware_data_key.vbprivk" \
-  -b "${KEYDIR}/firmware.keyblock" \
   -k "${KEYDIR}/kernel_subkey.vbpubk" \
+  -K "${KEYDIR}" \
   -v 1 \
   "${NO_B_SLOT}" "${NO_B_SLOT_SIGNED_IMG}"
 
@@ -321,9 +316,9 @@
 chmod +x "${CBFSTOOL_STUB}"
 
 if CBFSTOOL="${CBFSTOOL_STUB}" "${FUTILITY}" sign \
-  -s "${KEYDIR}/firmware_data_key.vbprivk" \
   -b "${KEYDIR}/firmware.keyblock" \
   -k "${KEYDIR}/kernel_subkey.vbpubk" \
+  -K "${KEYDIR}" \
   -v 1 \
   "${GOOD_CBFS}" "${TMP}.1.${GOOD_CBFS##*/}"
 then
@@ -351,9 +346,7 @@
 EOF
 
 if CBFSTOOL="${CBFSTOOL_STUB}" "${FUTILITY}" sign \
-  -s "${KEYDIR}/firmware_data_key.vbprivk" \
-  -b "${KEYDIR}/firmware.keyblock" \
-  -k "${KEYDIR}/kernel_subkey.vbpubk" \
+  -K "${KEYDIR}" \
   -v 1 \
   "${GOOD_CBFS}" "${TMP}.2.${GOOD_CBFS##*/}"
 then
@@ -379,9 +372,7 @@
   grep -q 'VBLOCK_A keyblock component is invalid' <<< "${FUTIL_OUTPUT}"
 
   FUTIL_OUTPUT="$("${FUTILITY}" sign \
-    -s "${KEYDIR}/firmware_data_key.vbprivk" \
-    -b "${KEYDIR}/firmware.keyblock" \
-    -k "${KEYDIR}/kernel_subkey.vbpubk" \
+    -K "${KEYDIR}" \
     "${BAD_IN}" "${BAD_OUT}" 2>&1)"
    grep -q 'VBLOCK_A keyblock is invalid' <<< "${FUTIL_OUTPUT}"
 
@@ -410,9 +401,7 @@
   grep -q 'VBLOCK_A is invalid' <<< "${FUTIL_OUTPUT}"
 
   FUTIL_OUTPUT="$("${FUTILITY}" sign \
-    -s "${KEYDIR}/firmware_data_key.vbprivk" \
-    -b "${KEYDIR}/firmware.keyblock" \
-    -k "${KEYDIR}/kernel_subkey.vbpubk" \
+    -K "${KEYDIR}" \
     "${BAD_IN}" "${BAD_OUT}" 2>&1)"
   grep -q 'VBLOCK_A preamble is invalid' <<< "${FUTIL_OUTPUT}"
 
@@ -441,9 +430,7 @@
   grep -q 'VBLOCK_A keyblock component is invalid' <<< "${FUTIL_OUTPUT}"
 
   FUTIL_OUTPUT="$(if "${FUTILITY}" sign \
-                       -s "${KEYDIR}/firmware_data_key.vbprivk" \
-                       -b "${KEYDIR}/firmware.keyblock" \
-                       -k "${KEYDIR}/kernel_subkey.vbpubk" \
+                       -K "${KEYDIR}" \
                        "${BAD_IN}" "${BAD_OUT}" 2>&1; \
                   then false; fi)"
   m="$(grep -c -E \
@@ -466,9 +453,7 @@
 grep -q 'VBLOCK_A is invalid' <<< "${FUTIL_OUTPUT}"
 
 FUTIL_OUTPUT="$(if "${FUTILITY}" sign \
-                     -s "${KEYDIR}/firmware_data_key.vbprivk" \
-                     -b "${KEYDIR}/firmware.keyblock" \
-                     -k "${KEYDIR}/kernel_subkey.vbpubk" \
+                     -K "${KEYDIR}" \
                      "${BAD_IN}" "${BAD_OUT}" 2>&1; \
                 then false; fi)"
 m="$(grep -c -E \
@@ -490,9 +475,7 @@
 grep -q 'VBLOCK_A is invalid' <<< "${FUTIL_OUTPUT}"
 
 FUTIL_OUTPUT="$(if "${FUTILITY}" sign \
-                     -s "${KEYDIR}/firmware_data_key.vbprivk" \
-                     -b "${KEYDIR}/firmware.keyblock" \
-                     -k "${KEYDIR}/kernel_subkey.vbpubk" \
+                     -K "${KEYDIR}" \
                      "${BAD_IN}" "${BAD_OUT}" 2>&1; \
                 then false; fi)"
 m="$(grep -c -E \
diff --git a/tests/futility/test_sign_fw_main.sh b/tests/futility/test_sign_fw_main.sh
index 6ab2083..e22f907 100755
--- a/tests/futility/test_sign_fw_main.sh
+++ b/tests/futility/test_sign_fw_main.sh
@@ -30,9 +30,7 @@
 
 # and the new way
 "${FUTILITY}" --debug sign \
-  --signprivate "${KEYDIR}/firmware_data_key.vbprivk" \
-  --keyblock "${KEYDIR}/firmware.keyblock" \
-  --kernelkey "${KEYDIR}/kernel_subkey.vbpubk" \
+  --keyset "${KEYDIR}" \
   --version 12 \
   --fv "${TMP}.fw_main" \
   --flags 42 \
diff --git a/tests/futility/test_sign_kernel.sh b/tests/futility/test_sign_kernel.sh
index 61b1c5a..bba1164 100755
--- a/tests/futility/test_sign_kernel.sh
+++ b/tests/futility/test_sign_kernel.sh
@@ -45,8 +45,7 @@
 
   # pack it up the new way
   "${FUTILITY}" --debug sign \
-    --keyblock "${DEVKEYS}/recovery_kernel.keyblock" \
-    --signprivate "${DEVKEYS}/recovery_kernel_data_key.vbprivk" \
+    --keyset "${DEVKEYS}/recovery_" \
     --version 1 \
     --config "${TMP}.config.txt" \
     --bootloader "${TMP}.bootloader.bin" \
@@ -84,7 +83,7 @@
 
   # repack it the new way
   "${FUTILITY}" --debug sign \
-    --signprivate "${DEVKEYS}/kernel_data_key.vbprivk" \
+    --keyset "${DEVKEYS}" \
     --keyblock "${DEVKEYS}/kernel.keyblock" \
     --version 2 \
     --pad "${padding}" \