Home | History | Annotate | Download | only in futility
      1 /* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
      2  * Use of this source code is governed by a BSD-style license that can be
      3  * found in the LICENSE file.
      4  *
      5  * Verified boot key block utility
      6  */
      7 
      8 #include <getopt.h>
      9 #include <stdint.h>
     10 #include <stdio.h>
     11 #include <stdlib.h>
     12 #include <string.h>
     13 
     14 #include "cryptolib.h"
     15 #include "futility.h"
     16 #include "host_common.h"
     17 #include "util_misc.h"
     18 #include "vboot_common.h"
     19 
     20 /* Command line options */
     21 enum {
     22 	OPT_MODE_PACK = 1000,
     23 	OPT_MODE_UNPACK,
     24 	OPT_DATAPUBKEY,
     25 	OPT_SIGNPUBKEY,
     26 	OPT_SIGNPRIVATE,
     27 	OPT_SIGNPRIVATE_PEM,
     28 	OPT_PEM_ALGORITHM,
     29 	OPT_EXTERNAL_SIGNER,
     30 	OPT_FLAGS,
     31 };
     32 
     33 static const struct option long_opts[] = {
     34 	{"pack", 1, 0, OPT_MODE_PACK},
     35 	{"unpack", 1, 0, OPT_MODE_UNPACK},
     36 	{"datapubkey", 1, 0, OPT_DATAPUBKEY},
     37 	{"signpubkey", 1, 0, OPT_SIGNPUBKEY},
     38 	{"signprivate", 1, 0, OPT_SIGNPRIVATE},
     39 	{"signprivate_pem", 1, 0, OPT_SIGNPRIVATE_PEM},
     40 	{"pem_algorithm", 1, 0, OPT_PEM_ALGORITHM},
     41 	{"externalsigner", 1, 0, OPT_EXTERNAL_SIGNER},
     42 	{"flags", 1, 0, OPT_FLAGS},
     43 	{NULL, 0, 0, 0}
     44 };
     45 
     46 static const char usage[] =
     47 	"\n"
     48 	"Usage:  " MYNAME " %s <--pack|--unpack> <file> [OPTIONS]\n"
     49 	"\n"
     50 	"For '--pack <file>', required OPTIONS are:\n"
     51 	"  --datapubkey <file>         Data public key in .vbpubk format\n"
     52 	"\n"
     53 	"Optional OPTIONS are:\n"
     54 	"  --signprivate <file>"
     55 	"        Signing private key in .vbprivk format.\n"
     56 	"OR\n"
     57 	"  --signprivate_pem <file>\n"
     58 	"  --pem_algorithm <algo>\n"
     59 	"        Signing private key in .pem format and algorithm id.\n"
     60 	"(If one of the above arguments is not specified, the keyblock will\n"
     61 	"not be signed.)\n"
     62 	"\n"
     63 	"  --flags <number>            Specifies allowed use conditions.\n"
     64 	"  --externalsigner \"cmd\""
     65 	"        Use an external program cmd to calculate the signatures.\n"
     66 	"\n"
     67 	"For '--unpack <file>', optional OPTIONS are:\n"
     68 	"  --signpubkey <file>"
     69 	"        Signing public key in .vbpubk format. This is required to\n"
     70 	"                                verify a signed keyblock.\n"
     71 	"  --datapubkey <file>"
     72 	"        Write the data public key to this file.\n\n";
     73 
     74 static void print_help(const char *progname)
     75 {
     76 	printf(usage, progname);
     77 }
     78 
     79 /* Pack a .keyblock */
     80 static int Pack(const char *outfile, const char *datapubkey,
     81 		const char *signprivate,
     82 		const char *signprivate_pem, uint64_t pem_algorithm,
     83 		uint64_t flags, const char *external_signer)
     84 {
     85 	VbPublicKey *data_key;
     86 	VbPrivateKey *signing_key = NULL;
     87 	VbKeyBlockHeader *block;
     88 
     89 	if (!outfile) {
     90 		fprintf(stderr,
     91 			"vbutil_keyblock: Must specify output filename.\n");
     92 		return 1;
     93 	}
     94 	if (!datapubkey) {
     95 		fprintf(stderr,
     96 			"vbutil_keyblock: Must specify data public key.\n");
     97 		return 1;
     98 	}
     99 
    100 	data_key = PublicKeyRead(datapubkey);
    101 	if (!data_key) {
    102 		fprintf(stderr, "vbutil_keyblock: Error reading data key.\n");
    103 		return 1;
    104 	}
    105 
    106 	if (signprivate_pem) {
    107 		if (pem_algorithm >= kNumAlgorithms) {
    108 			fprintf(stderr,
    109 				"vbutil_keyblock: Invalid --pem_algorithm %"
    110 				PRIu64 "\n", pem_algorithm);
    111 			return 1;
    112 		}
    113 		if (external_signer) {
    114 			/* External signing uses the PEM file directly. */
    115 			block = KeyBlockCreate_external(data_key,
    116 							signprivate_pem,
    117 							pem_algorithm, flags,
    118 							external_signer);
    119 		} else {
    120 			signing_key =
    121 			    PrivateKeyReadPem(signprivate_pem, pem_algorithm);
    122 			if (!signing_key) {
    123 				fprintf(stderr, "vbutil_keyblock:"
    124 					" Error reading signing key.\n");
    125 				return 1;
    126 			}
    127 			block = KeyBlockCreate(data_key, signing_key, flags);
    128 		}
    129 	} else {
    130 		if (signprivate) {
    131 			signing_key = PrivateKeyRead(signprivate);
    132 			if (!signing_key) {
    133 				fprintf(stderr, "vbutil_keyblock:"
    134 					" Error reading signing key.\n");
    135 				return 1;
    136 			}
    137 		}
    138 		block = KeyBlockCreate(data_key, signing_key, flags);
    139 	}
    140 
    141 	free(data_key);
    142 	if (signing_key)
    143 		free(signing_key);
    144 
    145 	if (0 != KeyBlockWrite(outfile, block)) {
    146 		fprintf(stderr, "vbutil_keyblock: Error writing key block.\n");
    147 		return 1;
    148 	}
    149 	free(block);
    150 	return 0;
    151 }
    152 
    153 static int Unpack(const char *infile, const char *datapubkey,
    154 		  const char *signpubkey)
    155 {
    156 	VbPublicKey *data_key;
    157 	VbPublicKey *sign_key = NULL;
    158 	VbKeyBlockHeader *block;
    159 
    160 	if (!infile) {
    161 		fprintf(stderr, "vbutil_keyblock: Must specify filename\n");
    162 		return 1;
    163 	}
    164 
    165 	block = KeyBlockRead(infile);
    166 	if (!block) {
    167 		fprintf(stderr, "vbutil_keyblock: Error reading key block.\n");
    168 		return 1;
    169 	}
    170 
    171 	/* If the block is signed, then verify it with the signing public key,
    172 	 * since KeyBlockRead() only verified the hash. */
    173 	if (block->key_block_signature.sig_size && signpubkey) {
    174 		sign_key = PublicKeyRead(signpubkey);
    175 		if (!sign_key) {
    176 			fprintf(stderr,
    177 				"vbutil_keyblock: Error reading signpubkey.\n");
    178 			return 1;
    179 		}
    180 		if (0 !=
    181 		    KeyBlockVerify(block, block->key_block_size, sign_key, 0)) {
    182 			fprintf(stderr, "vbutil_keyblock:"
    183 				" Error verifying key block.\n");
    184 			return 1;
    185 		}
    186 		free(sign_key);
    187 	}
    188 
    189 	printf("Key block file:       %s\n", infile);
    190 	printf("Signature             %s\n", sign_key ? "valid" : "ignored");
    191 	printf("Flags:                %" PRIu64 " ", block->key_block_flags);
    192 	if (block->key_block_flags & KEY_BLOCK_FLAG_DEVELOPER_0)
    193 		printf(" !DEV");
    194 	if (block->key_block_flags & KEY_BLOCK_FLAG_DEVELOPER_1)
    195 		printf(" DEV");
    196 	if (block->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_0)
    197 		printf(" !REC");
    198 	if (block->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_1)
    199 		printf(" REC");
    200 	printf("\n");
    201 
    202 	data_key = &block->data_key;
    203 	printf("Data key algorithm:   %" PRIu64 " %s\n", data_key->algorithm,
    204 	       (data_key->algorithm < kNumAlgorithms ?
    205 		algo_strings[data_key->algorithm] : "(invalid)"));
    206 	printf("Data key version:     %" PRIu64 "\n", data_key->key_version);
    207 	printf("Data key sha1sum:     ");
    208 	PrintPubKeySha1Sum(data_key);
    209 	printf("\n");
    210 
    211 	if (datapubkey) {
    212 		if (0 != PublicKeyWrite(datapubkey, data_key)) {
    213 			fprintf(stderr, "vbutil_keyblock:"
    214 				" unable to write public key\n");
    215 			return 1;
    216 		}
    217 	}
    218 
    219 	free(block);
    220 	return 0;
    221 }
    222 
    223 static int do_vbutil_keyblock(int argc, char *argv[])
    224 {
    225 
    226 	char *filename = NULL;
    227 	char *datapubkey = NULL;
    228 	char *signpubkey = NULL;
    229 	char *signprivate = NULL;
    230 	char *signprivate_pem = NULL;
    231 	char *external_signer = NULL;
    232 	uint64_t flags = 0;
    233 	uint64_t pem_algorithm = 0;
    234 	int is_pem_algorithm = 0;
    235 	int mode = 0;
    236 	int parse_error = 0;
    237 	char *e;
    238 	int i;
    239 
    240 	while ((i = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
    241 		switch (i) {
    242 		case '?':
    243 			/* Unhandled option */
    244 			printf("Unknown option\n");
    245 			parse_error = 1;
    246 			break;
    247 
    248 		case OPT_MODE_PACK:
    249 		case OPT_MODE_UNPACK:
    250 			mode = i;
    251 			filename = optarg;
    252 			break;
    253 
    254 		case OPT_DATAPUBKEY:
    255 			datapubkey = optarg;
    256 			break;
    257 
    258 		case OPT_SIGNPUBKEY:
    259 			signpubkey = optarg;
    260 			break;
    261 
    262 		case OPT_SIGNPRIVATE:
    263 			signprivate = optarg;
    264 			break;
    265 
    266 		case OPT_SIGNPRIVATE_PEM:
    267 			signprivate_pem = optarg;
    268 			break;
    269 
    270 		case OPT_PEM_ALGORITHM:
    271 			pem_algorithm = strtoul(optarg, &e, 0);
    272 			if (!*optarg || (e && *e)) {
    273 				fprintf(stderr, "Invalid --pem_algorithm\n");
    274 				parse_error = 1;
    275 			} else {
    276 				is_pem_algorithm = 1;
    277 			}
    278 			break;
    279 
    280 		case OPT_EXTERNAL_SIGNER:
    281 			external_signer = optarg;
    282 			break;
    283 
    284 		case OPT_FLAGS:
    285 			flags = strtoul(optarg, &e, 0);
    286 			if (!*optarg || (e && *e)) {
    287 				fprintf(stderr, "Invalid --flags\n");
    288 				parse_error = 1;
    289 			}
    290 			break;
    291 		}
    292 	}
    293 
    294 	/* Check if the right combination of options was provided. */
    295 	if (signprivate && signprivate_pem) {
    296 		fprintf(stderr,
    297 			"Only one of --signprivate or --signprivate_pem must"
    298 			" be specified\n");
    299 		parse_error = 1;
    300 	}
    301 
    302 	if (signprivate_pem && !is_pem_algorithm) {
    303 		fprintf(stderr, "--pem_algorithm must be used with"
    304 			" --signprivate_pem\n");
    305 		parse_error = 1;
    306 	}
    307 
    308 	if (external_signer && !signprivate_pem) {
    309 		fprintf(stderr,
    310 			"--externalsigner must be used with --signprivate_pem"
    311 			"\n");
    312 		parse_error = 1;
    313 	}
    314 
    315 	if (parse_error) {
    316 		print_help(argv[0]);
    317 		return 1;
    318 	}
    319 
    320 	switch (mode) {
    321 	case OPT_MODE_PACK:
    322 		return Pack(filename, datapubkey, signprivate,
    323 			    signprivate_pem, pem_algorithm,
    324 			    flags, external_signer);
    325 	case OPT_MODE_UNPACK:
    326 		return Unpack(filename, datapubkey, signpubkey);
    327 	default:
    328 		printf("Must specify a mode.\n");
    329 		print_help(argv[0]);
    330 		return 1;
    331 	}
    332 }
    333 
    334 DECLARE_FUTIL_COMMAND(vbutil_keyblock, do_vbutil_keyblock,
    335 		      VBOOT_VERSION_1_0,
    336 		      "Creates, signs, and verifies a keyblock",
    337 		      print_help);
    338