summaryrefslogtreecommitdiff
path: root/futility/cmd_sign.c
diff options
context:
space:
mode:
Diffstat (limited to 'futility/cmd_sign.c')
-rw-r--r--futility/cmd_sign.c174
1 files changed, 170 insertions, 4 deletions
diff --git a/futility/cmd_sign.c b/futility/cmd_sign.c
index f4c682ee..c28aee99 100644
--- a/futility/cmd_sign.c
+++ b/futility/cmd_sign.c
@@ -51,11 +51,16 @@ static struct local_data_s {
uint8_t *config_data;
uint64_t config_size;
enum arch_t arch;
+ int fv_specified;
uint32_t kloadaddr;
uint32_t padding;
int vblockonly;
char *outfile;
int create_new_outfile;
+ char *pem_signpriv;
+ int pem_algo_specified;
+ uint32_t pem_algo;
+ char *pem_external;
} option = {
.version = 1,
.arch = ARCH_UNSPECIFIED,
@@ -77,8 +82,39 @@ static int no_opt_if(int expr, const char *optname)
/* This wraps/signs a public key, producing a keyblock. */
int futil_cb_sign_pubkey(struct futil_traverse_state_s *state)
{
- fprintf(stderr, "Don't know how to sign %s yet\n", state->name);
- return 1;
+ VbPublicKey *data_key = (VbPublicKey *)state->my_area->buf;
+ VbKeyBlockHeader *vblock;
+
+ if (option.pem_signpriv) {
+ if (option.pem_external) {
+ /* External signing uses the PEM file directly. */
+ vblock = KeyBlockCreate_external(
+ data_key,
+ option.pem_signpriv,
+ option.pem_algo, option.flags,
+ option.pem_external);
+ } else {
+ option.signprivate = PrivateKeyReadPem(
+ option.pem_signpriv, option.pem_algo);
+ if (!option.signprivate) {
+ fprintf(stderr,
+ "Unable to read PEM signing key: %s\n",
+ strerror(errno));
+ return 1;
+ }
+ vblock = KeyBlockCreate(data_key, option.signprivate,
+ option.flags);
+ }
+ } else {
+ /* Not PEM. Should already have a signing key. */
+ vblock = KeyBlockCreate(data_key, option.signprivate,
+ option.flags);
+ }
+
+ /* Write it out */
+ return WriteSomeParts(option.outfile,
+ vblock, vblock->key_block_size,
+ NULL, 0);
}
/*
@@ -287,8 +323,36 @@ int futil_cb_resign_kernel_part(struct futil_traverse_state_s *state)
int futil_cb_sign_raw_firmware(struct futil_traverse_state_s *state)
{
- fprintf(stderr, "Don't know how to sign %s yet\n", state->name);
- return 1;
+ VbSignature *body_sig;
+ VbFirmwarePreambleHeader *preamble;
+ int rv;
+
+ body_sig = CalculateSignature(state->my_area->buf, state->my_area->len,
+ option.signprivate);
+ if (!body_sig) {
+ fprintf(stderr, "Error calculating body signature\n");
+ return 1;
+ }
+
+ preamble = CreateFirmwarePreamble(option.version,
+ option.kernel_subkey,
+ body_sig,
+ option.signprivate,
+ option.flags);
+ if (!preamble) {
+ fprintf(stderr, "Error creating firmware preamble.\n");
+ free(body_sig);
+ return 1;
+ }
+
+ rv = WriteSomeParts(option.outfile,
+ option.keyblock, option.keyblock->key_block_size,
+ preamble, preamble->preamble_size);
+
+ free(preamble);
+ free(body_sig);
+
+ return rv;
}
@@ -446,10 +510,53 @@ static const char usage[] = "\n"
"\n"
"Where INFILE is a\n"
"\n"
+ " public key (.vbpubk); OUTFILE is a keyblock\n"
+ " raw firmware blob (FW_MAIN_A/B); OUTFILE is a VBLOCK_A/B\n"
" complete firmware image (bios.bin)\n"
" raw linux kernel; OUTFILE is a kernel partition image\n"
" kernel partition image (/dev/sda2, /dev/mmcblk0p2)\n";
+static const char usage_pubkey[] = "\n"
+ "-----------------------------------------------------------------\n"
+ "To sign a public key / create a new keyblock:\n"
+ "\n"
+ "Required PARAMS:\n"
+ " [--datapubkey] INFILE The public key to wrap\n"
+ " [--outfile] OUTFILE The resulting keyblock\n"
+ "\n"
+ "Optional PARAMS:\n"
+ " A private signing key, specified as either\n"
+ " -s|--signprivate FILE.vbprivk Signing key in .vbprivk format\n"
+ " Or\n"
+ " --pem_signpriv FILE.pem Signing key in PEM format...\n"
+ " --pem_algo NUM AND the algorithm to use (0 - %d)\n"
+ "\n"
+ " If a signing key is not given, the keyblock will not be signed (duh)."
+ "\n\n"
+ "And these, too:\n\n"
+ " -f|--flags NUM Flags specifying use conditions\n"
+ " --pem_external PROGRAM"
+ " External program to compute the signature\n"
+ " (requires a PEM signing key)\n";
+
+static const char usage_fw_main[] = "\n"
+ "-----------------------------------------------------------------\n"
+ "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"
+ " -f|--flags NUM The preamble flags value"
+ " (default is 0)\n";
+
static const char usage_bios[] = "\n"
"-----------------------------------------------------------------\n"
"To sign a complete firmware image (bios.bin):\n"
@@ -528,6 +635,8 @@ static const char usage_old_kpart[] = "\n"
static void print_help(const char *prog)
{
printf(usage, prog);
+ printf(usage_pubkey, kNumAlgorithms - 1);
+ puts(usage_fw_main);
printf(usage_bios, option.version);
printf(usage_new_kpart, option.kloadaddr, option.padding);
printf(usage_old_kpart, option.padding);
@@ -542,6 +651,9 @@ enum no_short_opts {
OPT_ARCH,
OPT_KLOADADDR,
OPT_PADDING,
+ OPT_PEM_SIGNPRIV,
+ OPT_PEM_ALGO,
+ OPT_PEM_EXTERNAL,
};
static const struct option long_opts[] = {
@@ -565,6 +677,9 @@ static const struct option long_opts[] = {
{"arch", 1, NULL, OPT_ARCH},
{"kloadaddr", 1, NULL, OPT_KLOADADDR},
{"pad", 1, NULL, OPT_PADDING},
+ {"pem_signpriv", 1, NULL, OPT_PEM_SIGNPRIV},
+ {"pem_algo", 1, NULL, OPT_PEM_ALGO},
+ {"pem_external", 1, NULL, OPT_PEM_EXTERNAL},
{"vblockonly", 0, &option.vblockonly, 1},
{"debug", 0, &debugging_enabled, 1},
{NULL, 0, NULL, 0},
@@ -648,6 +763,9 @@ static int do_sign(int argc, char *argv[])
case 'l':
option.loemid = optarg;
break;
+ case OPT_FV:
+ option.fv_specified = 1;
+ /* fallthrough */
case OPT_INFILE: /* aka "--vmlinuz" */
inout_file_count++;
infile = optarg;
@@ -711,6 +829,23 @@ static int do_sign(int argc, char *argv[])
errorcnt++;
}
break;
+ case OPT_PEM_SIGNPRIV:
+ option.pem_signpriv = optarg;
+ break;
+ case OPT_PEM_ALGO:
+ option.pem_algo_specified = 1;
+ option.pem_algo = strtoul(optarg, &e, 0);
+ if (!*optarg || (e && *e) ||
+ (option.pem_algo >= kNumAlgorithms)) {
+ fprintf(stderr,
+ "Invalid --pem_algo \"%s\"\n", optarg);
+ errorcnt++;
+ }
+ break;
+ case OPT_PEM_EXTERNAL:
+ option.pem_external = optarg;
+ break;
+
case '?':
if (optopt)
fprintf(stderr, "Unrecognized option: -%c\n",
@@ -752,6 +887,8 @@ static int do_sign(int argc, char *argv[])
if (option.bootloader_data || option.config_data
|| option.arch != ARCH_UNSPECIFIED)
type = FILE_TYPE_RAW_KERNEL;
+ else if (option.kernel_subkey || option.fv_specified)
+ type = FILE_TYPE_RAW_FIRMWARE;
}
/* Check the arguments for the type of thing we want to sign */
@@ -761,6 +898,28 @@ static int do_sign(int argc, char *argv[])
"Unable to determine the type of the input file\n");
errorcnt++;
goto done;
+ case FILE_TYPE_PUBKEY:
+ option.create_new_outfile = 1;
+ if (option.signprivate && option.pem_signpriv) {
+ fprintf(stderr,
+ "Only one of --signprivate and --pem_signpriv"
+ " can be specified\n");
+ errorcnt++;
+ }
+ if ((option.signprivate && option.pem_algo_specified) ||
+ (option.pem_signpriv && !option.pem_algo_specified)) {
+ fprintf(stderr, "--pem_algo must be used with"
+ " --pem_signpriv\n");
+ errorcnt++;
+ }
+ if (option.pem_external && !option.pem_signpriv) {
+ fprintf(stderr, "--pem_external must be used with"
+ " --pem_signpriv\n");
+ errorcnt++;
+ }
+ /* We'll wait to read the PEM file, since the external signer
+ * may want to read it instead. */
+ break;
case FILE_TYPE_KEYBLOCK:
fprintf(stderr, "Resigning a keyblock is kind of pointless.\n");
fprintf(stderr, "Just create a new one.\n");
@@ -786,6 +945,13 @@ static int do_sign(int argc, char *argv[])
if (option.vblockonly)
option.create_new_outfile = 1;
break;
+ case FILE_TYPE_RAW_FIRMWARE:
+ option.create_new_outfile = 1;
+ errorcnt += no_opt_if(!option.signprivate, "signprivate");
+ errorcnt += no_opt_if(!option.keyblock, "keyblock");
+ errorcnt += no_opt_if(!option.kernel_subkey, "kernelkey");
+ errorcnt += no_opt_if(!option.version_specified, "version");
+ break;
case FILE_TYPE_RAW_KERNEL:
option.create_new_outfile = 1;
errorcnt += no_opt_if(!option.signprivate, "signprivate");