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 
print_help(const char * progname)74 static void print_help(const char *progname)
75 {
76 	printf(usage, progname);
77 }
78 
79 /* Pack a .keyblock */
Pack(const char * outfile,const char * datapubkey,const char * signprivate,const char * signprivate_pem,uint64_t pem_algorithm,uint64_t flags,const char * external_signer)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 
Unpack(const char * infile,const char * datapubkey,const char * signpubkey)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 
do_vbutil_keyblock(int argc,char * argv[])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