xref: /aosp_15_r20/external/vboot_reference/futility/cmd_gscvd.c (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
1 /* Copyright 2021 The ChromiumOS Authors
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file.
4  */
5 
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <getopt.h>
9 #include <openssl/bn.h>
10 #include <openssl/pem.h>
11 #include <stdbool.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 
15 #include "fmap.h"
16 #include "futility.h"
17 #include "gsc_ro.h"
18 #include "host_key21.h"
19 #include "host_keyblock.h"
20 #include "host_misc.h"
21 #include "host_signature.h"
22 #include "host_p11.h"
23 
24 /*
25  * for testing purposes let's use
26  * - tests/devkeys/arv_root.vbprivk as the root private key
27  * - tests/devkeys/arv_root.vbpubk as the root public key
28  *   used for signing of the platform public key
29  * - tests/devkeys/arv_platform.vbprivk signing platform key
30  * - tests/devkeys/arv_platform.vbpubk - public key used for signature
31  *       verification
32  *------------
33  * Command to create the signed public key block in ~/tmp/packed:
34  *
35   ./build/futility/futility vbutil_keyblock --pack ~/tmp/packed \
36       --datapubkey  tests/devkeys/arv_platform.vbpubk \
37       --signprivate tests/devkeys/arv_root.vbprivk
38  *------------
39  * Command to fill RO_GSCVD FMAP area in an AP firmware file. The input AP
40  *   firmware file is ~/tmp/image-guybrush.serial.bin, the output signed
41  *   AP firmware file is ~/tmp/guybrush-signed:
42  *
43   ./build/futility/futility gscvd --outfile ~/tmp/guybrush-signed \
44     -R 818100:10000,f00000:100,f80000:2000,f8c000:1000,0x00804000:0x00000800 \
45     -k ~/tmp/packed -p tests/devkeys/arv_platform.vbprivk -b 5a5a4352  \
46     -r tests/devkeys/arv_root.vbpubk ~/tmp/image-guybrush.serial.bin
47  *------------
48  * Command to validate a previously signed AP firmware file. The hash is the
49  *  sha256sum of tests/devkeys/kernel_subkey.vbpubk:
50  *
51   build/futility/futility gscvd ~/tmp/guybrush-signed \
52    3d74429f35be8d34bcb425d4397e2218e6961afed456a78ce30047f5b54ed158
53  */
54 
55 /* Command line options processing support. */
56 enum no_short_opts {
57 	OPT_OUTFILE = 1000,
58 	OPT_RO_GSCVD_FILE = 1001,
59 };
60 
61 static const struct option long_opts[] = {
62 	/* name       hasarg *flag  val */
63 	{"add_gbb",       0, NULL, 'G'},
64 	{"board_id",      1, NULL, 'b'},
65 	{"help",          0, NULL, 'h'},
66 	{"keyblock",      1, NULL, 'k'},
67 	{"outfile",       1, NULL, OPT_OUTFILE},
68 	{"platform_priv", 1, NULL, 'p'},
69 	{"ranges",        1, NULL, 'R'},
70 	{"gscvd_out",     1, NULL, OPT_RO_GSCVD_FILE},
71 	{"root_pub_key",  1, NULL, 'r'},
72 	{}
73 };
74 
75 static const char *short_opts = "R:Gb:hk:p:r:";
76 
77 static const char usage[] =
78 	"\n"
79 	"This utility creates an RO verification space in the Chrome OS AP\n"
80 	"firmware image, allows to validate a previously prepared image\n"
81 	"containing the RO verification space, and prints out the hash of the\n"
82 	"payload of the root public key.\n\n"
83 	"Create a new GSCVD from scratch:\n"
84 	"  "MYNAME" gscvd -R <ranges> PARAMS <firmware image>\n\n"
85 	"Re-sign an existing GSCVD with new keys, preserving ranges:\n"
86 	"  "MYNAME" gscvd PARAMS <firmware image>\n\n"
87 	"Validate an existing GSCVD with given root key hash:\n"
88 	"  "MYNAME" gscvd <firmware image> [<root key hash in hex>]\n\n"
89 	"Print the hash of a public root key:\n"
90 	"  "MYNAME" gscvd -r <root key .vpubk file>\n\n"
91 	"Required PARAMS:\n"
92 	"  -b|--board_id  <string|hex>      The Board ID of the board for\n"
93 	"                                     which the image is signed.\n"
94 	"                                     Can be passed as a 4-letter\n"
95 	"                                     string or a hexadecimal number.\n"
96 	"  -r|--root_pub_key  <file>        The main public key, in .vbpubk\n"
97 	"                                     format, used to verify platform\n"
98 	"                                     key\n"
99 	"  -k|--keyblock      <file>        Signed platform public key in\n"
100 	"                                     .keyblock format, used for run\n"
101 	"                                     time RO verifcation\n"
102 	"  -p|--platform_priv <file>        Private platform key in .vbprivk\n"
103 	"                                     format, used for signing RO\n"
104 	"                                     verification data\n"
105 	"Optional PARAMS:\n"
106 	"  -G|--add_gbb                     Add the `GBB` FMAP section to the\n"
107 	"                                     ranges covered by the signature.\n"
108 	"                                     This option takes special care\n"
109 	"                                     to exclude the HWID (and its\n"
110 	"                                     digest) from this range.\n"
111 	"  -R|--ranges        STRING        Comma separated colon delimited\n"
112 	"                                     hex tuples <offset>:<size>, the\n"
113 	"                                     areas of the RO covered by the\n"
114 	"                                     signature, if omitted the\n"
115 	"                                     ranges are expected to be\n"
116 	"                                     present in the GSCVD section\n"
117 	"                                     of the input file\n"
118 	"  [--outfile]        OUTFILE       Output firmware image containing\n"
119 	"                                     RO verification information\n"
120 	"  [--gscvd_out]      GSCVD_FILE    A binary blob containing just the\n"
121 	"                                     unpadded RO_GSCVD section\n"
122 	"  -h|--help                        Print this message\n\n";
123 
124 /* Structure helping to keep track of the file mapped into memory. */
125 struct file_buf {
126 	uint32_t len;
127 	uint8_t *data;
128 	int fd;
129 	FmapAreaHeader *ro_gscvd;
130 	/* Cached GBB information. */
131 	const FmapAreaHeader *gbb_area;
132 	uint32_t gbb_maxlen;
133 };
134 
135 /*
136  * Max number of RO ranges to cover. 32 is more than enough, this must be kept
137  * in sync with
138  * - AP_RO_MAX_NUM_RANGES in cr50/common/ap_ro_integrity_check.c
139  * - MAX_RO_RANGES in ti50/common/capsules/src/ap_ro_verification/gscvd.rs
140  */
141 #define MAX_RANGES 32
142 
143 /*
144  * Container keeping track of the set of ranges to include in hash
145  * calculation.
146  */
147 struct gscvd_ro_ranges {
148 	size_t range_count;
149 	struct gscvd_ro_range ranges[MAX_RANGES];
150 };
151 
152 /**
153  * Load the AP firmware file into memory.
154  *
155  * Map the requested file into memory, find RO_GSCVD area in the file, and
156  * cache the information in the passed in file_buf structure.
157  *
158  * @param file_name  name of the AP firmware file
159  * @param file_buf   pointer to the helper structure keeping information about
160  *                   the file
161  *
162  * @return 0 on success 1 on failure.
163  */
load_ap_firmware(const char * file_name,struct file_buf * file,int mode)164 static int load_ap_firmware(const char *file_name, struct file_buf *file,
165 			int mode)
166 {
167 	memset(file, 0, sizeof(*file));
168 
169 	if (futil_open_and_map_file(file_name, &file->fd, mode, &file->data,
170 				    &file->len))
171 		return 1;
172 
173 	if (!fmap_find_by_name(file->data, file->len, NULL, "RO_GSCVD",
174 			       &file->ro_gscvd)) {
175 		ERROR("Could not find RO_GSCVD in the FMAP\n");
176 		futil_unmap_and_close_file(file->fd, mode, file->data,
177 					   file->len);
178 		file->fd = -1;
179 		file->data = NULL;
180 		file->len = 0;
181 		return 1;
182 	}
183 
184 	/*
185 	 * Try finding FMAP gbb area and validating the GBB. It's not a
186 	 * failure if GBB is not found, it might not be required after all.
187 	 */
188 	FmapAreaHeader *area;
189 	while (fmap_find_by_name(file->data, file->len, NULL, "GBB", &area)) {
190 		struct vb2_gbb_header *gbb;
191 		uint32_t maxlen;
192 
193 		gbb = (void *)(file->data + area->area_offset);
194 
195 		if (!futil_valid_gbb_header(gbb, area->area_size, &maxlen)) {
196 			ERROR("GBB is invalid.\n");
197 			break;
198 		}
199 
200 		/*
201 		 * This implementation relies on the fact that no meaningful
202 		 * fields come after the `hwid_digest` field in the header. If
203 		 * we ever make new GBB versions that add more fields, the
204 		 * code below and in add_gbb() needs to be adapted. Older
205 		 * versions than 1.2 or GBBs with a bmpblk are not expected
206 		 * with GSCVD images.
207 		 */
208 		if (gbb->major_version != 1 || gbb->minor_version != 2 ||
209 		    gbb->bmpfv_size != 0) {
210 			ERROR("Unsupported GBB version.\n");
211 			break;
212 		}
213 
214 
215 		file->gbb_area = area;
216 		file->gbb_maxlen = maxlen;
217 
218 		break;
219 	}
220 
221 	return 0;
222 }
223 
224 /**
225  * Check if the passed in offset falls into the passed in FMAP area.
226  */
in_range(uint32_t offset,const FmapAreaHeader * ah)227 static bool in_range(uint32_t offset, const FmapAreaHeader *ah)
228 {
229 	return (offset >= ah->area_offset) &&
230 	       (offset <= (ah->area_offset + ah->area_size));
231 }
232 
233 /**
234  * Check if the passed in range fits into the passed in FMAP area.
235  */
range_fits(const struct gscvd_ro_range * range,const FmapAreaHeader * ah)236 static bool range_fits(const struct gscvd_ro_range *range,
237 		       const FmapAreaHeader *ah)
238 {
239 	if (in_range(range->offset, ah) &&
240 	    in_range(range->offset + range->size, ah))
241 		return true;
242 
243 	return false;
244 }
245 
246 /**
247  * Check if the passed in range overlaps with the area.
248  *
249  * @param range  pointer to the range to check
250  * @param offset  offset of the area to check against
251  * @param size  size of the area to check against
252  *
253  * @return true if range overlaps with the area, false otherwise.
254  */
range_overlaps(const struct gscvd_ro_range * range,uint32_t offset,size_t size)255 static bool range_overlaps(const struct gscvd_ro_range *range, uint32_t offset,
256 			   size_t size)
257 {
258 	if (((range->offset + range->size) <= offset) ||
259 	    (offset + size) <= range->offset)
260 		return false;
261 
262 	ERROR("Range %x..+%x overlaps with %x..+%zx\n", range->offset,
263 	      range->size, offset, size);
264 
265 	return true;
266 }
267 
268 /*
269  * Check validity of the passed in ranges.
270  *
271  * All ranges must
272  * - fit into the WP_RO FMAP area
273  * - not overlap with the RO_GSCVD FMAP area
274  * - not overlap with each other
275  *
276  * @param ranges - pointer to the container of ranges to check
277  * @param file - pointer to the file layout descriptor
278  *
279  * @return zero on success, -1 on failures
280  */
verify_ranges(const struct gscvd_ro_ranges * ranges,const struct file_buf * file)281 static int verify_ranges(const struct gscvd_ro_ranges *ranges,
282 			 const struct file_buf *file)
283 {
284 	size_t i;
285 	FmapAreaHeader *wp_ro;
286 	FmapAreaHeader *si_all;
287 	int errorcount;
288 
289 	if (!fmap_find_by_name(file->data, file->len, NULL, "WP_RO", &wp_ro)) {
290 		ERROR("Could not find WP_RO in the FMAP\n");
291 		return 1;
292 	}
293 
294 	/* Intel boards can have an SI_ALL region that's not in WP_RO but is
295 	   protected by platform-specific mechanisms, and may still contain
296 	   components that we want to protect from physical attack. */
297 	if (!fmap_find_by_name(file->data, file->len, NULL, "SI_ALL", &si_all))
298 		si_all = NULL;
299 
300 	errorcount = 0;
301 	for (i = 0; i < ranges->range_count; i++) {
302 		size_t j;
303 
304 		/* Must fit into WP_RO or SI_ALL. */
305 		if (!range_fits(ranges->ranges + i, wp_ro) &&
306 		    (!si_all || !range_fits(ranges->ranges + i, si_all))) {
307 			ERROR("Range %#x..+%#x does not fit in WP_RO/SI_ALL\n",
308 				ranges->ranges[i].offset,
309 				ranges->ranges[i].size);
310 			errorcount++;
311 		}
312 
313 		/* Must not overlap with RO_GSCVD. */
314 		if (range_overlaps(ranges->ranges + i,
315 				   file->ro_gscvd->area_offset,
316 				   file->ro_gscvd->area_size))
317 			errorcount++;
318 
319 		/* The last range is nothing to compare against. */
320 		if (i == ranges->range_count - 1)
321 			break;
322 
323 		/* Must not overlap with all following ranges. */
324 		for (j = i + 1; j < ranges->range_count; j++)
325 			if (range_overlaps(ranges->ranges + i,
326 					   ranges->ranges[j].offset,
327 					   ranges->ranges[j].size))
328 				errorcount++;
329 	}
330 
331 	return errorcount ? -1 : 0;
332 }
333 
334 /**
335  * Parse range specification supplied by the user.
336  *
337  * The input is a string of the following format:
338  * <hex base>:<hex size>[,<hex base>:<hex size>[,...]]
339  *
340  * @param input  user input, part of the command line
341  * @param output  pointer to the ranges container
342  *
343  * @return zero on success, -1 on failure
344  */
parse_ranges(const char * input,struct gscvd_ro_ranges * output)345 static int parse_ranges(const char *input, struct gscvd_ro_ranges *output)
346 {
347 	char *cursor;
348 	char *delim;
349 	char *str = strdup(input);
350 	int rv = 0;
351 
352 	if (!str) {
353 		ERROR("Failed to allocate memory for ranges string copy!\n");
354 		return -1;
355 	}
356 
357 	cursor = str;
358 	do {
359 		char *colon;
360 		char *e;
361 
362 		if (output->range_count >= ARRAY_SIZE(output->ranges)) {
363 			ERROR("Too many ranges!\n");
364 			rv = -1;
365 			break;
366 		}
367 
368 		delim = strchr(cursor, ',');
369 		if (delim)
370 			*delim = '\0';
371 		colon = strchr(cursor, ':');
372 		if (!colon) {
373 			rv = -1;
374 			break;
375 		}
376 		*colon = '\0';
377 
378 		errno = 0;
379 		output->ranges[output->range_count].offset =
380 			strtol(cursor, &e, 16);
381 		if (errno || *e) {
382 			rv = -1;
383 			break;
384 		}
385 
386 		output->ranges[output->range_count].size =
387 			strtol(colon + 1, &e, 16);
388 		if (errno || *e) {
389 			rv = -1;
390 			break;
391 		}
392 
393 		output->range_count++;
394 		cursor = delim + 1;
395 		/* Iterate until there is no more commas. */
396 	} while (delim);
397 
398 	free(str);
399 	if (rv)
400 		ERROR("Misformatted ranges string\n");
401 
402 	return rv;
403 }
404 
405 /**
406  * Add GBB to ranges.
407  *
408  * Splits the `GBB` FMAP section into separate ranges to exclude the HWID string
409  * and the `hwid_digest` field in the header. Will also exclude the empty area
410  * behind the end of the actual GBB data.
411  *
412  * @param ranges pointer to the ranges container
413  * @param file   pointer to the AP firmware file layout descriptor
414  */
add_gbb(struct gscvd_ro_ranges * ranges,const struct file_buf * file)415 static int add_gbb(struct gscvd_ro_ranges *ranges, const struct file_buf *file)
416 {
417 	if (!file->gbb_area) {
418 		ERROR("Could not find a GBB area in the FMAP.\n");
419 		return 1;
420 	}
421 
422 	const struct vb2_gbb_header *gbb = (void *)(file->data +
423 					      file->gbb_area->area_offset);
424 	uint32_t lower_key_offset = VB2_MIN(gbb->rootkey_offset,
425 					    gbb->recovery_key_offset);
426 	if (gbb->hwid_offset > lower_key_offset) {
427 		ERROR("Weird GBB layout (HWID should come first)\n");
428 		return 1;
429 	}
430 
431 	if (ranges->range_count >= ARRAY_SIZE(ranges->ranges) - 2) {
432 		ERROR("Too many ranges, can't fit GBB!\n");
433 		return 1;
434 	}
435 
436 	ranges->ranges[ranges->range_count].offset =
437 		file->gbb_area->area_offset;
438 	ranges->ranges[ranges->range_count].size =
439 		offsetof(struct vb2_gbb_header, hwid_digest);
440 	ranges->range_count++;
441 
442 	ranges->ranges[ranges->range_count].offset =
443 		file->gbb_area->area_offset + lower_key_offset;
444 	ranges->ranges[ranges->range_count].size =
445 		file->gbb_maxlen - lower_key_offset;
446 	ranges->range_count++;
447 
448 	return 0;
449 }
450 
451 /**
452  * Extend AP RO hash digest with data from an address range.
453  *
454  * If the flags_offset value is non zero and happens to fall into the passed
455  * in range, do not read values from flash in the flags_offset..+flags_size
456  * range, instead feed zeros to the hashing function.
457  *
458  * NOTE that flags are expected to fully fit into the range, cases of overlap
459  * are not supported.
460  *
461  * @param ap_firmware_file  pointer to the AP firmware file layout descriptor
462  * @param dc  pointer to the hash calculating context
463  * @param offset  offset of the beginning of the range in AP SPI flash
464  * @param size  size of the range
465  * @param flags_offset  if nonzero - offset of the GBB flags field in
466  *			AP SPI flash
467  *
468  * @return VB2_SUCCESS or digest extension error, if any.
469  */
extend_digest(const struct file_buf * ap_firmware_file,struct vb2_digest_context * dc,uint32_t offset,uint32_t size,uint32_t flags_offset)470 static vb2_error_t extend_digest(const struct file_buf *ap_firmware_file,
471 				 struct vb2_digest_context *dc,
472 				 uint32_t offset,
473 				 uint32_t size,
474 				 uint32_t flags_offset)
475 {
476 	/* Define it as array to simplify calling vb2_digest_extend() below. */
477 	const uint8_t flags[sizeof(vb2_gbb_flags_t)] = {0};
478 
479 	VB2_DEBUG("%s: %#x..+%#x\n", __func__, offset, size);
480 
481 	if (flags_offset &&
482 	    (flags_offset >= offset) &&
483 	    (flags_offset < (offset + size))) {
484 		uint32_t flags_size;
485 		vb2_error_t rv;
486 
487 		/*
488 		 * This range includes GBB flags, which need to be zeroized.
489 		 *
490 		 * First get the hash of up to the flags.
491 		 */
492 		rv = vb2_digest_extend(dc, ap_firmware_file->data + offset,
493 				       flags_offset - offset);
494 		if (rv != VB2_SUCCESS)
495 			return rv;
496 
497 		size -= flags_offset - offset;
498 		offset = flags_offset;
499 
500 		/* Now hash the flag space, maybe partially. */
501 		flags_size = VB2_MIN(size, sizeof(flags));
502 		rv = vb2_digest_extend(dc, flags, flags_size);
503 		if (rv != VB2_SUCCESS)
504 			return rv;
505 
506 		/* Update size and offset to cover the rest of the range. */
507 		size -= flags_size;
508 
509 		offset += flags_size;
510 	}
511 
512 	return vb2_digest_extend(dc,ap_firmware_file->data + offset, size);
513 }
514 
515 /**
516  * Calculate hash of the RO ranges.
517  *
518  * @param ap_firmware_file  pointer to the AP firmware file layout descriptor
519  * @param ranges  pointer to the container of ranges to include in hash
520  *		  calculation
521  * @param hash_alg  algorithm to use for hashing
522  * @param digest  memory to copy the calculated hash to
523  * @param digest_ size requested size of the digest, padded with zeros if the
524  *	          SHA digest size is smaller than digest_size
525  * @param override_gbb_flags  if true, replace GBB flags value with zero
526  *
527  * @return zero on success, -1 on failure.
528  */
calculate_ranges_digest(const struct file_buf * ap_firmware_file,const struct gscvd_ro_ranges * ranges,enum vb2_hash_algorithm hash_alg,void * digest,size_t digest_size,bool override_gbb_flags)529 static int calculate_ranges_digest(const struct file_buf *ap_firmware_file,
530 				   const struct gscvd_ro_ranges *ranges,
531 				   enum vb2_hash_algorithm hash_alg,
532 				   void *digest, size_t digest_size,
533 				   bool override_gbb_flags)
534 {
535 	struct vb2_digest_context dc;
536 	size_t i;
537 	uint32_t flags_offset = 0;
538 
539 	if (override_gbb_flags && ap_firmware_file->gbb_area)
540 		flags_offset = offsetof(struct vb2_gbb_header, flags) +
541 			ap_firmware_file->gbb_area->area_offset;
542 
543 	/* Calculate the ranges digest. */
544 	if (vb2_digest_init(&dc, false, hash_alg, 0) != VB2_SUCCESS) {
545 		ERROR("Failed to init digest!\n");
546 		return 1;
547 	}
548 
549 	for (i = 0; i < ranges->range_count; i++) {
550 		if (extend_digest(ap_firmware_file, &dc,
551 				  ranges->ranges[i].offset,
552 				  ranges->ranges[i].size,
553 				  flags_offset) != VB2_SUCCESS) {
554 			ERROR("Failed to extend digest!\n");
555 			return -1;
556 		}
557 	}
558 
559 	memset(digest, 0, digest_size);
560 	if (vb2_digest_finalize(&dc, digest, digest_size) != VB2_SUCCESS) {
561 		ERROR("Failed to finalize digest!\n");
562 		return -1;
563 	}
564 
565 	return 0;
566 }
567 
568 /**
569  * Build GSC verification data.
570  *
571  * Calculate size of the structure including the signature and the root key,
572  * allocate memory, fill up the structure, calculate AP RO ranges digest and
573  * then the GVD signature.
574  *
575  * @param ap_firmware_file  pointer to the AP firmware file layout descriptor
576  * @param ranges  pointer to the container of ranges to include in verification
577  * @param root_pubk  pointer to the root pubk container
578  * @param privk   pointer to the private key to use for signing
579  * @param board_id  Board ID value to use.
580  *
581  * @return pointer to the created GVD (to be freed by the caller) on success,
582  *         NULL on failure.
583  */
584 static
create_gvd(struct file_buf * ap_firmware_file,struct gscvd_ro_ranges * ranges,const struct vb2_packed_key * root_pubk,const struct vb2_private_key * privk,uint32_t board_id)585 struct gsc_verification_data *create_gvd(struct file_buf *ap_firmware_file,
586 					 struct gscvd_ro_ranges *ranges,
587 					 const struct vb2_packed_key *root_pubk,
588 					 const struct vb2_private_key *privk,
589 					 uint32_t board_id)
590 {
591 	struct gsc_verification_data *gvd;
592 	size_t total_size;
593 	size_t sig_size;
594 	size_t ranges_size;
595 	struct vb2_signature *sig;
596 	const FmapHeader *fmh;
597 
598 	sig_size = vb2_rsa_sig_size(privk->sig_alg);
599 	ranges_size = ranges->range_count * sizeof(struct gscvd_ro_range);
600 	total_size = sizeof(struct gsc_verification_data) +
601 		root_pubk->key_size + sig_size + ranges_size;
602 
603 	gvd = calloc(total_size, 1);
604 
605 	if (!gvd) {
606 		ERROR("Failed to allocate %zd bytes for gvd\n", total_size);
607 		return NULL;
608 	}
609 
610 	gvd->gv_magic = GSC_VD_MAGIC;
611 	gvd->size = total_size;
612 	gvd->gsc_board_id = board_id;
613 	gvd->rollback_counter = GSC_VD_ROLLBACK_COUNTER;
614 
615 	/* Guaranteed to succeed. */
616 	fmh = fmap_find(ap_firmware_file->data, ap_firmware_file->len);
617 
618 	gvd->fmap_location = (uintptr_t)fmh - (uintptr_t)ap_firmware_file->data;
619 
620 	gvd->hash_alg = VB2_HASH_SHA256;
621 
622 	if (calculate_ranges_digest(ap_firmware_file, ranges, gvd->hash_alg,
623 				    gvd->ranges_digest,
624 				    sizeof(gvd->ranges_digest),
625 				    true)) {
626 		free(gvd);
627 		return NULL;
628 	}
629 
630 	/* Prepare signature header. */
631 	vb2_init_signature(&gvd->sig_header,
632 			   (uint8_t *)(gvd + 1) + ranges_size,
633 			   sig_size,
634 			   sizeof(struct gsc_verification_data) + ranges_size);
635 
636 	/* Copy root key into the structure. */
637 	vb2_init_packed_key(&gvd->root_key_header,
638 			    (uint8_t *)(gvd + 1) + ranges_size + sig_size,
639 			    root_pubk->key_size);
640 	vb2_copy_packed_key(&gvd->root_key_header, root_pubk);
641 
642 	/* Copy ranges into the ranges section. */
643 	gvd->range_count = ranges->range_count;
644 	memcpy(gvd->ranges, ranges->ranges, ranges_size);
645 
646 	sig = vb2_calculate_signature((uint8_t *)gvd,
647 				      sizeof(struct gsc_verification_data) +
648 				      ranges_size, privk);
649 	if (!sig) {
650 		ERROR("Failed to calculate signature\n");
651 		free(gvd);
652 		return NULL;
653 	}
654 
655 	/* Copy signature body into GVD after some basic checks. */
656 	if ((sig_size == sig->sig_size) &&
657 	    (gvd->sig_header.data_size == sig->data_size)) {
658 		vb2_copy_signature(&gvd->sig_header, sig);
659 	} else {
660 		ERROR("Inconsistent signature headers\n");
661 		free(sig);
662 		free(gvd);
663 		return NULL;
664 	}
665 
666 	free(sig);
667 
668 	return gvd;
669 }
670 
671 /**
672  * Fill RO_GSCVD FMAP area.
673  *
674  * All trust chain components have been verified, AP RO sections digest
675  * calculated, and GVD signature created; put it all together in the dedicated
676  * FMAP area and save in a binary blob if requested.
677  *
678  * @param ap_firmware_file  pointer to the AP firmware file layout descriptor
679  * @param gvd  pointer to the GVD header
680  * @param keyblock  pointer to the keyblock container
681  * @param gscvd_file_name  if not NULL the name of the file to save the
682  *			RO_GSCVD section in.
683  * @return zero on success, -1 on failure
684  */
fill_gvd_area(struct file_buf * ap_firmware_file,struct gsc_verification_data * gvd,struct vb2_keyblock * keyblock,const char * gscvd_file_name)685 static int fill_gvd_area(struct file_buf *ap_firmware_file,
686 			 struct gsc_verification_data *gvd,
687 			 struct vb2_keyblock *keyblock,
688 			 const char *gscvd_file_name)
689 {
690 	size_t total;
691 	uint8_t *cursor;
692 
693 	/* How much room is needed for the whole thing? */
694 	total = gvd->size + keyblock->keyblock_size;
695 
696 	if (total > ap_firmware_file->ro_gscvd->area_size) {
697 		ERROR("GVD section does not fit, %zd > %d\n",
698 		      total, ap_firmware_file->ro_gscvd->area_size);
699 		return -1;
700 	}
701 
702 	cursor = ap_firmware_file->data +
703 		 ap_firmware_file->ro_gscvd->area_offset;
704 
705 	/* Copy GSC verification data */
706 	memcpy(cursor, gvd, gvd->size);
707 	cursor += gvd->size;
708 
709 	/* Keyblock, size includes everything. */
710 	memcpy(cursor, keyblock, keyblock->keyblock_size);
711 
712 	if (gscvd_file_name) {
713 		if (vb2_write_file(gscvd_file_name, cursor - gvd->size,
714 				   total) != VB2_SUCCESS)
715 			return -1;
716 	}
717 	return 0;
718 }
719 
720 /**
721  * Initialize a work buffer structure.
722  *
723  * Embedded vboot reference code does not use malloc/free, it uses the so
724  * called work buffer structure to provide a poor man's memory management
725  * tool. This program uses some of the embedded library functions, let's
726  * implement work buffer support to keep the embedded code happy.
727  *
728  * @param wb  pointer to the workubffer structure to initialize
729  * @param size  size of the buffer to allocate
730  *
731  * @return pointer to the allocated buffer on success, NULL on failure.
732  */
init_wb(struct vb2_workbuf * wb,size_t size)733 static void *init_wb(struct vb2_workbuf *wb, size_t size)
734 {
735 	void *buf = malloc(size);
736 
737 	if (!buf)
738 		ERROR("Failed to allocate workblock of %zd\n", size);
739 	else
740 		vb2_workbuf_init(wb, buf, size);
741 
742 	return buf;
743 }
744 
745 /**
746  * Validate that platform key keyblock was signed by the root key.
747  *
748  * This function performs the same step the GSC is supposed to perform:
749  * validate the platform key keyblock signature using the root public key.
750  *
751  * @param root_pubk  pointer to the root public key container
752  * @param kblock  pointer to the platform public key keyblock
753  *
754  * @return 0 on success, -1 on failure
755  */
validate_pubk_signature(const struct vb2_packed_key * root_pubk,struct vb2_keyblock * kblock)756 static int validate_pubk_signature(const struct vb2_packed_key *root_pubk,
757 				   struct vb2_keyblock *kblock)
758 {
759 	struct vb2_public_key pubk;
760 	struct vb2_workbuf wb;
761 	uint32_t kbsize;
762 	int rv;
763 	void *buf;
764 
765 	if (vb2_unpack_key(&pubk, root_pubk) != VB2_SUCCESS) {
766 		ERROR("Failed to unpack public key\n");
767 		return -1;
768 	}
769 
770 	/* Let's create an ample sized work buffer. */
771 	buf = init_wb(&wb, 8192);
772 	if (!buf)
773 		return -1;
774 
775 	rv = -1;
776 	do {
777 		void *work;
778 
779 		kbsize = kblock->keyblock_size;
780 		work = vb2_workbuf_alloc(&wb, kbsize);
781 		if (!work) {
782 			ERROR("Failed to allocate workblock space %d\n",
783 			      kbsize);
784 			break;
785 		}
786 
787 		memcpy(work, kblock, kbsize);
788 
789 		if (vb2_verify_keyblock(work, kbsize, &pubk, &wb) !=
790 		    VB2_SUCCESS) {
791 			ERROR("Root and keyblock mismatch\n");
792 			break;
793 		}
794 
795 		rv = 0;
796 	} while (false);
797 
798 	free(buf);
799 
800 	return rv;
801 }
802 
803 /**
804  * Validate that private and public parts of the platform key match.
805  *
806  * This is a fairly routine validation, the N components of the private and
807  * public RSA keys are compared.
808  *
809  * @param keyblock  pointer to the keyblock containing the public key
810  * @param plat_privk  pointer to the matching private key
811  *
812  * @return 0 on success, nonzero on failure
813  */
validate_privk(struct vb2_keyblock * kblock,struct vb2_private_key * plat_privk)814 static int validate_privk(struct vb2_keyblock *kblock,
815 			  struct vb2_private_key *plat_privk)
816 {
817 	BIGNUM *privn;
818 	BIGNUM *pubn;
819 	struct vb2_public_key pubk;
820 	int rv = -1; // Speculatively set to error return value.
821 
822 	privn = pubn = NULL;
823 
824 	if (plat_privk->key_location != PRIVATE_KEY_P11) {
825 		RSA_get0_key(plat_privk->rsa_private_key, (const BIGNUM **)&privn,
826 			     NULL, NULL);
827 	} else {
828 		uint32_t size;
829 		uint8_t *bytes;
830 
831 		bytes = pkcs11_get_modulus(plat_privk->p11_key, &size);
832 		if (bytes == NULL) {
833 			ERROR("Failed to retrieve private key modulus\n");
834 			return rv;
835 		}
836 
837 		privn = BN_bin2bn(bytes, size, NULL);
838 		free(bytes);
839 
840 		if (!privn) {
841 			ERROR("Failed to allocate BN for priv key modulus\n");
842 			return rv;
843 		}
844 	}
845 
846 	if (vb2_unpack_key(&pubk, &kblock->data_key) != VB2_SUCCESS) {
847 		ERROR("Failed to unpack public key\n");
848 		return rv;
849 	}
850 
851 	pubn = BN_lebin2bn((uint8_t *)pubk.n, vb2_rsa_sig_size(pubk.sig_alg),
852 			   NULL);
853 	if (pubn) {
854 		rv = BN_cmp(pubn, privn);
855 		BN_free(pubn);
856 		if (rv)
857 			ERROR("Public/private key N mismatch!\n");
858 	} else {
859 		ERROR("Failed to allocate BN for pub key modulus\n");
860 	}
861 
862 	if (plat_privk->key_location == PRIVATE_KEY_P11)
863 		BN_free(privn);
864 
865 	return rv;
866 }
867 
868 /**
869  * Copy ranges from AP firmware file into gscvd_ro_ranges container
870  *
871  * While copying the ranges verify that they do not overlap.
872  *
873  * @param ap_firmware_file  pointer to the AP firmware file layout descriptor
874  * @param gvd  pointer to the GVD header followed by the ranges
875  * @param ranges  pointer to the ranges container to copy ranges to
876  *
877  * @return 0 on successful copy nonzero on errors.
878  */
copy_ranges(const struct file_buf * ap_firmware_file,const struct gsc_verification_data * gvd,struct gscvd_ro_ranges * ranges)879 static int copy_ranges(const struct file_buf *ap_firmware_file,
880 		       const struct gsc_verification_data *gvd,
881 		       struct gscvd_ro_ranges *ranges)
882 {
883 	ranges->range_count = gvd->range_count;
884 	memcpy(ranges->ranges, gvd->ranges,
885 	       sizeof(ranges->ranges[0]) * ranges->range_count);
886 
887 	return verify_ranges(ranges, ap_firmware_file);
888 }
889 
890 /**
891  * Basic validation of GVD included in a AP firmware file.
892  *
893  * This is not a cryptographic verification, just a check that the structure
894  * makes sense and the expected values are found in certain fields.
895  *
896  * @param gvd  pointer to the GVD header followed by the ranges
897  * @param ap_firmware_file  pointer to the AP firmware file layout descriptor
898  *
899  * @return zero on success, -1 on failure.
900  */
validate_gvd(const struct gsc_verification_data * gvd,const struct file_buf * ap_firmware_file)901 static int validate_gvd(const struct gsc_verification_data *gvd,
902 			const struct file_buf *ap_firmware_file)
903 {
904 	const FmapHeader *fmh;
905 
906 	if (gvd->gv_magic != GSC_VD_MAGIC) {
907 		ERROR("Incorrect gscvd magic %x\n", gvd->gv_magic);
908 		return -1;
909 	}
910 
911 	if (!gvd->range_count || (gvd->range_count > MAX_RANGES)) {
912 		ERROR("Incorrect gscvd range count %d\n", gvd->range_count);
913 		return -1;
914 	}
915 
916 	/* Guaranteed to succeed. */
917 	fmh = fmap_find(ap_firmware_file->data, ap_firmware_file->len);
918 
919 	if (gvd->fmap_location !=
920 	    ((uintptr_t)fmh - (uintptr_t)ap_firmware_file->data)) {
921 		ERROR("Incorrect gscvd fmap offset %x\n", gvd->fmap_location);
922 		return -1;
923 	}
924 
925 	/* Make sure signature and root key fit. */
926 	if (vb2_verify_signature_inside(gvd, gvd->size, &gvd->sig_header) !=
927 	    VB2_SUCCESS) {
928 		ERROR("Corrupted signature header in GVD\n");
929 		return -1;
930 	}
931 
932 	if (vb2_verify_packed_key_inside(gvd, gvd->size,
933 					 &gvd->root_key_header) !=
934 	    VB2_SUCCESS) {
935 		ERROR("Corrupted root key header in GVD\n");
936 		return -1;
937 	}
938 
939 	return 0;
940 }
941 
942 /**
943  * Validate GVD signature.
944  *
945  * Given the entire GVD space (header plus ranges array), the signature and
946  * the public key, verify that the signature matches.
947  *
948  * @param gvd  pointer to gsc_verification_data followed by the ranges array
949  * @param gvd_signature  pointer to the vb2 signature container
950  * @param packedk  pointer to the keyblock containing the public key
951  *
952  * @return zero on success, non-zero on failure
953  */
validate_gvd_signature(struct gsc_verification_data * gvd,const struct vb2_packed_key * packedk)954 static int validate_gvd_signature(struct gsc_verification_data *gvd,
955 				  const struct vb2_packed_key *packedk)
956 {
957 	struct vb2_workbuf wb;
958 	void *buf;
959 	int rv;
960 	struct vb2_public_key pubk;
961 	size_t signed_size;
962 
963 	/* Extract public key from the public key keyblock. */
964 	if (vb2_unpack_key(&pubk, packedk) != VB2_SUCCESS) {
965 		ERROR("Failed to unpack public key\n");
966 		return -1;
967 	}
968 
969 	/* Let's create an ample sized work buffer. */
970 	buf = init_wb(&wb, 8192);
971 	if (!buf)
972 		return -1;
973 
974 	signed_size = sizeof(struct gsc_verification_data) +
975 		gvd->range_count * sizeof(gvd->ranges[0]);
976 
977 	rv = vb2_verify_data((const uint8_t *)gvd, signed_size,
978 			     &gvd->sig_header,
979 			     &pubk, &wb);
980 
981 	free(buf);
982 	return rv;
983 }
984 
985 /*
986  * Try retrieving GVD ranges from the passed in AP firmware file.
987  *
988  * The passed in ranges structure is set to the set of ranges retrieved from
989  * the firmware file, if any.
990  */
try_retrieving_ranges_from_the_image(const char * file_name,struct gscvd_ro_ranges * ranges,struct file_buf * ap_firmware_file)991 static void try_retrieving_ranges_from_the_image(const char *file_name,
992 						 struct gscvd_ro_ranges *ranges,
993 						 struct file_buf
994 						 *ap_firmware_file)
995 {
996 	struct gsc_verification_data *gvd;
997 	size_t i;
998 
999 	ranges->range_count = 0;
1000 
1001 	/* Look for ranges in GVD and copy them if found. */
1002 	gvd = (struct gsc_verification_data
1003 	       *)(ap_firmware_file->data +
1004 		  ap_firmware_file->ro_gscvd->area_offset);
1005 
1006 	if (validate_gvd(gvd, ap_firmware_file))
1007 		return;
1008 
1009 	if (copy_ranges(ap_firmware_file, gvd, ranges))
1010 		return;
1011 
1012 	if (!ranges->range_count) {
1013 		printf("No ranges found in the input file\n");
1014 	} else {
1015 		printf("Will re-sign the following %zd ranges:\n",
1016 		       ranges->range_count);
1017 		for (i = 0; i < ranges->range_count; i++) {
1018 			printf("%08x:%08x\n",
1019 			       ranges->ranges[i].offset,
1020 			       ranges->ranges[i].size);
1021 		}
1022 	}
1023 }
1024 
1025 /*
1026  * Calculate ranges digest and compare it with the value stored in gvd, with
1027  * or without ignoring GBB flags, as requested by the caller.
1028  *
1029  * @return zero on success, nonzero on failure.
1030  */
validate_digest(struct file_buf * ap_firmware_file,const struct gscvd_ro_ranges * ranges,const struct gsc_verification_data * gvd,bool override_gbb_flags)1031 static int validate_digest(struct file_buf *ap_firmware_file,
1032 			   const struct gscvd_ro_ranges *ranges,
1033 			   const struct gsc_verification_data *gvd,
1034 			   bool override_gbb_flags)
1035 {
1036 	uint8_t digest[sizeof(gvd->ranges_digest)];
1037 
1038 	if (calculate_ranges_digest(ap_firmware_file, ranges,
1039 				    gvd->hash_alg, digest,
1040 				    sizeof(digest),
1041 				    override_gbb_flags)) {
1042 		return 1;
1043 	}
1044 
1045 	return memcmp(digest, gvd->ranges_digest, sizeof(digest));
1046 }
1047 
1048 /*
1049  * Validate GVD of the passed in AP firmware file and possibly the root key hash
1050  *
1051  * The input parameters are the subset of the command line, the first argv
1052  * string is the AP firmware file name, the second string, if present, is the
1053  * hash of the root public key included in the RO_GSCVD area of the AP
1054  * firmware file.
1055  *
1056  * @return zero on success, nonzero on failure.
1057  */
validate_gscvd(int argc,char * argv[])1058 static int validate_gscvd(int argc, char *argv[])
1059 {
1060 	struct file_buf ap_firmware_file;
1061 	int rv;
1062 	struct gscvd_ro_ranges ranges;
1063 	struct gsc_verification_data *gvd;
1064 	const char *file_name;
1065 	struct vb2_hash root_key_digest = { .algo = VB2_HASH_SHA256 };
1066 
1067 	/* Guaranteed to be available. */
1068 	file_name = argv[0];
1069 
1070 	if (argc > 1)
1071 		parse_digest_or_die(root_key_digest.sha256,
1072 				    sizeof(root_key_digest.sha256),
1073 				    argv[1]);
1074 
1075 	do {
1076 		struct vb2_keyblock *kblock;
1077 
1078 		rv = -1; /* Speculative, will be cleared on success. */
1079 
1080 		if (load_ap_firmware(file_name, &ap_firmware_file, FILE_RO))
1081 			break;
1082 
1083 		/* Copy ranges from gscvd to local structure. */
1084 		gvd = (struct gsc_verification_data
1085 			       *)(ap_firmware_file.data +
1086 				  ap_firmware_file.ro_gscvd->area_offset);
1087 
1088 		if (validate_gvd(gvd, &ap_firmware_file))
1089 			break;
1090 
1091 		if (copy_ranges(&ap_firmware_file, gvd, &ranges))
1092 			break;
1093 
1094 		/* First try validating without ignoring GBB flags. */
1095 		if (validate_digest(&ap_firmware_file, &ranges, gvd, false)) {
1096 			/* It failed, maybe GBB flags are not cleared yet. */
1097 			if (validate_digest(&ap_firmware_file, &ranges,
1098 					    gvd, true)) {
1099 				ERROR("Ranges digest mismatch\n");
1100 				break;
1101 			}
1102 			WARN("Ranges digest matches with zeroed GBB flags\n");
1103 		}
1104 
1105 		/* Find the keyblock. */
1106 		kblock = (struct vb2_keyblock *)((uintptr_t)gvd + gvd->size);
1107 
1108 		if ((argc > 1) && (vb2_hash_verify(false,
1109 				vb2_packed_key_data(&gvd->root_key_header),
1110 				gvd->root_key_header.key_size,
1111 				&root_key_digest) != VB2_SUCCESS)) {
1112 			ERROR("Sha256 mismatch\n");
1113 			break;
1114 		}
1115 
1116 		if (validate_pubk_signature(&gvd->root_key_header, kblock)) {
1117 			ERROR("Keyblock not signed by root key\n");
1118 			break;
1119 		}
1120 
1121 		if (validate_gvd_signature(gvd, &kblock->data_key)) {
1122 			ERROR("GVD not signed by platform key\n");
1123 			break;
1124 		}
1125 
1126 		rv = 0;
1127 	} while (false);
1128 
1129 	if (ap_firmware_file.fd != -1)
1130 		futil_unmap_and_close_file(ap_firmware_file.fd, FILE_RO,
1131 					   ap_firmware_file.data,
1132 					   ap_firmware_file.len);
1133 
1134 	return rv;
1135 }
1136 
1137 /**
1138  * Calculate and report sha256 hash of the public key body.
1139  *
1140  * The hash will be incorporated into GVC firmware to allow it to validate the
1141  * root key.
1142  *
1143  * @param pubk pointer to the public key to process.
1144  */
dump_pubk_hash(const struct vb2_packed_key * pubk)1145 static void dump_pubk_hash(const struct vb2_packed_key *pubk)
1146 {
1147 	struct vb2_hash hash;
1148 	size_t i;
1149 
1150 	vb2_hash_calculate(false, vb2_packed_key_data(pubk), pubk->key_size,
1151 			   VB2_HASH_SHA256, &hash);
1152 
1153 	printf("Root key body sha256 hash:\n");
1154 
1155 	for (i = 0; i < sizeof(hash.sha256); i++)
1156 		printf("%02x", hash.sha256[i]);
1157 
1158 	printf("\n");
1159 }
1160 
1161 /**
1162  * The main function of this futilty option.
1163  *
1164  * See the usage string for input details.
1165  *
1166  * @return zero on success, nonzero on failure.
1167  */
do_gscvd(int argc,char * argv[])1168 static int do_gscvd(int argc, char *argv[])
1169 {
1170 	int i;
1171 	int longindex;
1172 	bool do_gbb = false;
1173 	char *infile = NULL;
1174 	char *outfile = NULL;
1175 	char *work_file = NULL;
1176 	struct gscvd_ro_ranges ranges;
1177 	int errorcount = 0;
1178 	struct vb2_packed_key *root_pubk = NULL;
1179 	struct vb2_keyblock *kblock = NULL;
1180 	struct vb2_private_key *plat_privk = NULL;
1181 	struct gsc_verification_data *gvd = NULL;
1182 	struct file_buf ap_firmware_file = { .fd = -1 };
1183 	uint32_t board_id = UINT32_MAX;
1184 	char *ro_gscvd_file = NULL;
1185 	int rv = 0;
1186 
1187 	ranges.range_count = 0;
1188 
1189 	while ((i = getopt_long(argc, argv, short_opts, long_opts,
1190 				&longindex)) != -1) {
1191 		switch (i) {
1192 		case OPT_OUTFILE:
1193 			outfile = optarg;
1194 			break;
1195 		case OPT_RO_GSCVD_FILE:
1196 			ro_gscvd_file = optarg;
1197 			break;
1198 		case 'R':
1199 			if (parse_ranges(optarg, &ranges)) {
1200 				ERROR("Could not parse ranges\n");
1201 				/* Error message has been already printed. */
1202 				errorcount++;
1203 			}
1204 			break;
1205 		case 'G':
1206 			do_gbb = true;
1207 			break;
1208 		case 'b': {
1209 			char *e;
1210 			long long bid;
1211 
1212 			if (strlen(optarg) == 4) {
1213 				board_id = optarg[0] << 24 |
1214 					   optarg[1] << 16 |
1215 					   optarg[2] << 8 |
1216 					   optarg[3];
1217 				break;
1218 			}
1219 
1220 			bid = strtoull(optarg, &e, 16);
1221 			if (*e || (bid >= UINT32_MAX)) {
1222 				ERROR("Cannot parse Board ID '%s'\n",
1223 				      optarg);
1224 				errorcount++;
1225 			} else {
1226 				board_id = (uint32_t)bid;
1227 			}
1228 			break;
1229 		}
1230 		case 'r':
1231 			root_pubk = vb2_read_packed_key(optarg);
1232 			if (!root_pubk) {
1233 				ERROR("Could not read %s\n", optarg);
1234 				errorcount++;
1235 			}
1236 			break;
1237 		case 'k':
1238 			kblock = vb2_read_keyblock(optarg);
1239 			if (!kblock) {
1240 				ERROR("Could not read %s\n", optarg);
1241 				errorcount++;
1242 			}
1243 			break;
1244 		case 'p':
1245 			plat_privk = vb2_read_private_key(optarg);
1246 			if (!plat_privk) {
1247 				ERROR("Could not read %s\n", optarg);
1248 				errorcount++;
1249 			}
1250 			break;
1251 		case 'h':
1252 			printf("%s", usage);
1253 			return 0;
1254 		case '?':
1255 			if (optopt)
1256 				ERROR("Unrecognized option: -%c\n", optopt);
1257 			else
1258 				ERROR("Unrecognized option: %s\n",
1259 				      argv[optind - 1]);
1260 			errorcount++;
1261 			break;
1262 		case ':':
1263 			ERROR("Missing argument to -%c\n", optopt);
1264 			errorcount++;
1265 			break;
1266 		case 0: /* handled option */
1267 			break;
1268 		default:
1269 			FATAL("Unrecognized getopt output: %d\n", i);
1270 		}
1271 	}
1272 
1273 	if ((optind == 1) && (argc > 1)) {
1274 		if (ro_gscvd_file) {
1275 			ERROR("Unexpected --gscvd_out in command line\n");
1276 			goto usage_out;
1277 		}
1278 		/* This must be a validation request. */
1279 		return validate_gscvd(argc - 1, argv + 1);
1280 	}
1281 
1282 	if (errorcount) /* Error message(s) should have been printed by now. */
1283 		goto usage_out;
1284 
1285 	if (!root_pubk) {
1286 		ERROR("Missing --root_pub_key argument\n");
1287 		goto usage_out;
1288 	} else if (argc == 3) {
1289 		if (ro_gscvd_file) {
1290 			ERROR("Unexpected --gscvd_out in command line\n");
1291 			goto usage_out;
1292 		}
1293 		/*
1294 		 * This is a request to print out the hash of the root pub key
1295 		 * payload.
1296 		 */
1297 		dump_pubk_hash(root_pubk);
1298 		return 0;
1299 	}
1300 
1301 	if (optind != (argc - 1)) {
1302 		ERROR("Misformatted command line\n");
1303 		goto usage_out;
1304 	}
1305 
1306 	infile = argv[optind];
1307 
1308 	if (!kblock) {
1309 		ERROR("Missing --keyblock argument\n");
1310 		goto usage_out;
1311 	}
1312 
1313 	if (!plat_privk) {
1314 		ERROR("Missing --platform_priv argument\n");
1315 		goto usage_out;
1316 	}
1317 
1318 	if (board_id == UINT32_MAX) {
1319 		ERROR("Missing --board_id argument\n");
1320 		goto usage_out;
1321 	}
1322 
1323 	if (outfile) {
1324 		if (futil_copy_file(infile, outfile) < 0)
1325 			exit(1);
1326 		work_file = outfile;
1327 	} else {
1328 		work_file = infile;
1329 	}
1330 
1331 	do {
1332 		rv = 1; /* Speculative, will be cleared on success. */
1333 
1334 		if (validate_pubk_signature(root_pubk, kblock))
1335 			break;
1336 
1337 		if (validate_privk(kblock, plat_privk))
1338 			break;
1339 
1340 		if (load_ap_firmware(work_file, &ap_firmware_file, FILE_RW))
1341 			break;
1342 
1343 		if (!ranges.range_count)
1344 			try_retrieving_ranges_from_the_image(infile,
1345 							     &ranges,
1346 							     &ap_firmware_file);
1347 
1348 		if (!ranges.range_count && !do_gbb) {
1349 			ERROR("Missing --ranges argument and no ranges in the input file\n");
1350 			break;
1351 		}
1352 
1353 		if (do_gbb && add_gbb(&ranges, &ap_firmware_file))
1354 			break;
1355 
1356 		if (verify_ranges(&ranges, &ap_firmware_file))
1357 			break;
1358 
1359 		gvd = create_gvd(&ap_firmware_file, &ranges,
1360 				 root_pubk, plat_privk, board_id);
1361 		if (!gvd)
1362 			break;
1363 
1364 		if (fill_gvd_area(&ap_firmware_file, gvd,
1365 				  kblock, ro_gscvd_file))
1366 			break;
1367 
1368 		rv = 0;
1369 	} while (false);
1370 
1371 	free(gvd);
1372 	free(root_pubk);
1373 	free(kblock);
1374 	vb2_free_private_key(plat_privk);
1375 
1376 	if (ap_firmware_file.fd != -1)
1377 		futil_unmap_and_close_file(ap_firmware_file.fd, FILE_RW,
1378 					   ap_firmware_file.data,
1379 					   ap_firmware_file.len);
1380 
1381 	return rv;
1382 
1383 usage_out:
1384 	fputs(usage, stderr);
1385 	return 1;
1386 }
1387 
1388 DECLARE_FUTIL_COMMAND(gscvd, do_gscvd, VBOOT_VERSION_2_1,
1389 		      "Create RO verification structure");
1390