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