mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-11-26 19:25:02 +00:00
Revert "vboot_reference: Change EC signing to apply new FMAP areas." This reverts commit ef0ab3a616acc0d8d18d45c3145852f38744b856 Change-Id: I9248b76c0896190ac6febac9d8239a709554b1ee Reviewed-on: https://gerrit.chromium.org/gerrit/28107 Tested-by: Vic Yang <victoryang@chromium.org> Commit-Ready: Vic Yang <victoryang@chromium.org> Reviewed-by: Vic Yang <victoryang@chromium.org>
533 lines
15 KiB
C
533 lines
15 KiB
C
/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*
|
|
* Verified boot utility for EC firmware
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include "cryptolib.h"
|
|
#include "fmap.h"
|
|
#include "host_common.h"
|
|
#include "vboot_common.h"
|
|
|
|
|
|
/* Command line options */
|
|
enum {
|
|
OPT_MODE_SIGN = 1000,
|
|
OPT_MODE_VERIFY,
|
|
OPT_KEYBLOCK,
|
|
OPT_SIGNPUBKEY,
|
|
OPT_SIGNPRIVATE,
|
|
OPT_VERSION,
|
|
OPT_FV,
|
|
OPT_KERNELKEY,
|
|
OPT_FLAGS,
|
|
OPT_NAME,
|
|
};
|
|
|
|
static struct option long_opts[] = {
|
|
{"sign", 1, 0, OPT_MODE_SIGN },
|
|
{"verify", 1, 0, OPT_MODE_VERIFY },
|
|
{"keyblock", 1, 0, OPT_KEYBLOCK },
|
|
{"signpubkey", 1, 0, OPT_SIGNPUBKEY },
|
|
{"signprivate", 1, 0, OPT_SIGNPRIVATE },
|
|
{"version", 1, 0, OPT_VERSION },
|
|
{"flags", 1, 0, OPT_FLAGS },
|
|
{"name", 1, 0, OPT_NAME },
|
|
{NULL, 0, 0, 0}
|
|
};
|
|
|
|
|
|
/* Print help and return error */
|
|
static int PrintHelp(void) {
|
|
|
|
puts("vbutil_ec - Verified boot signing utility for EC firmware\n"
|
|
"\n"
|
|
"This will sign, re-sign, or test a complete EC firmware image.\n"
|
|
"The EC image is initially completely unsigned. To make it bootable\n"
|
|
"the pubic root key must be installed in the RO section, and each RW\n"
|
|
"section must be signed with the appropriate private keys.\n"
|
|
"\n"
|
|
"To sign an image: vbutil_ec --sign <file> [OPTIONS]\n"
|
|
"\n"
|
|
"For signing, these options are required:\n"
|
|
"\n"
|
|
" --keyblock <file> Key block in .keyblock format\n"
|
|
" --signprivate <file> Signing private key in .vbprivk format\n"
|
|
" --version <number> Firmware version\n"
|
|
"\n"
|
|
"If the RO public key has not been installed, you will also need\n"
|
|
"\n"
|
|
" --signpubkey <file> Signing public key in .vbpubk format\n"
|
|
"\n"
|
|
"Optional args are:\n"
|
|
"\n"
|
|
" --flags <number> Preamble flags (defaults to 0)\n"
|
|
" --name <string> Human-readable description\n"
|
|
"\n"
|
|
"\n"
|
|
"To verify an image: vbutil_ec --verify <file>\n"
|
|
"\n");
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int FindInFmap(FmapHeader *fh, const char *name,
|
|
uint8_t *base, uint64_t base_size,
|
|
uint8_t **data, uint64_t *size) {
|
|
const FmapAreaHeader *ah;
|
|
int i;
|
|
|
|
ah = (FmapAreaHeader *)(fh + 1);
|
|
for (i = 0; i < fh->fmap_nareas; i++)
|
|
if (!strncmp(ah[i].area_name, name, FMAP_NAMELEN)) {
|
|
if (ah[i].area_size + ah[i].area_offset > base_size) {
|
|
printf("FMAP region %s extends off image file\n", name);
|
|
return 0;
|
|
}
|
|
if (data)
|
|
*data = base + ah[i].area_offset;
|
|
if (size)
|
|
*size = ah[i].area_size;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int GoodKey(VbPublicKey *key, uint64_t region_size)
|
|
{
|
|
uint64_t key_size;
|
|
|
|
if (0 != VerifyPublicKeyInside(key, region_size, key))
|
|
return 0;
|
|
|
|
if (key->algorithm >= kNumAlgorithms)
|
|
return 0;
|
|
|
|
/* Currently, TPM only supports 16-bit version */
|
|
if (key->key_version > 0xFFFF)
|
|
return 0;
|
|
|
|
if (!RSAProcessedKeySize(key->algorithm, &key_size) ||
|
|
key_size != key->key_size)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* We build the image file with a non-FF byte at the end of each RW firmware,
|
|
* just so we can do this. */
|
|
static uint64_t FindImageEnd(uint8_t *data, uint64_t size)
|
|
{
|
|
for (size-- ; size && data[size] == 0xff; size--)
|
|
;
|
|
return size;
|
|
}
|
|
|
|
static void SignImage(const char *filename,
|
|
VbKeyBlockHeader *key_block, uint64_t key_block_size,
|
|
VbPrivateKey *privkey, uint64_t version,
|
|
VbPublicKey *pubkey, uint32_t preamble_flags,
|
|
const char *name) {
|
|
struct stat sb;
|
|
int fd;
|
|
void *image;
|
|
uint64_t image_size;
|
|
FmapHeader* fmap;
|
|
VbECPreambleHeader *preamble;
|
|
uint8_t *fv_data = 0;
|
|
uint8_t *vblock_data = 0;
|
|
uint64_t fv_size, vblock_size;
|
|
VbSignature* body_digest;
|
|
|
|
if (name && strlen(name)+1 > sizeof(preamble->name))
|
|
VbExError("Name string is too long\n");
|
|
|
|
if (0 != stat(filename, &sb))
|
|
VbExError("Can't stat %s: %s\n", filename, strerror(errno));
|
|
|
|
fd = open(filename, O_RDWR);
|
|
if (fd < 0)
|
|
VbExError("Can't open %s: %s\n", filename, strerror(errno));
|
|
|
|
image = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
|
|
if (image == (void *)-1)
|
|
VbExError("Can't mmap %s: %s\n", filename, strerror(errno));
|
|
close(fd); /* done with this now */
|
|
|
|
fmap = (FmapHeader *)FmapFind(image, sb.st_size);
|
|
if (!fmap)
|
|
VbExError("File %s doesn't have an FMAP - can't continue.\n");
|
|
|
|
if (fmap->fmap_size > sb.st_size)
|
|
VbExError("FMAP is bigger than file size (%ld vs %ld)\n",
|
|
fmap->fmap_size, sb.st_size);
|
|
|
|
image_size = sb.st_size;
|
|
|
|
/* Install pubkey if provided */
|
|
if (pubkey) {
|
|
if (!FindInFmap(fmap, "ROOT_KEY", image, image_size,
|
|
&vblock_data, &vblock_size))
|
|
VbExError("Can't find ROOT_KEY in %s\n", filename);
|
|
|
|
if (pubkey->key_offset + pubkey->key_size > vblock_size)
|
|
VbExError("ROOT_KEY is too small for pubkey (%d bytes, needs %d)\n",
|
|
vblock_size, pubkey->key_offset + pubkey->key_size);
|
|
|
|
memcpy(vblock_data, pubkey, pubkey->key_offset + pubkey->key_size);
|
|
}
|
|
|
|
|
|
/* Sign FW A */
|
|
if (!FindInFmap(fmap, "FW_MAIN_A", image, image_size, &fv_data, &fv_size))
|
|
VbExError("Can't find FW_MAIN_A in %s\n", filename);
|
|
|
|
if (!FindInFmap(fmap, "VBLOCK_A", image, image_size,
|
|
&vblock_data, &vblock_size))
|
|
VbExError("Can't find VBLOCK_A in %s\n", filename);
|
|
|
|
fv_size = FindImageEnd(fv_data, fv_size);
|
|
|
|
body_digest = CalculateHash(fv_data, fv_size, privkey);
|
|
if (!body_digest)
|
|
VbExError("Error calculating body digest\n");
|
|
|
|
preamble = CreateECPreamble(version, body_digest, privkey,
|
|
preamble_flags, name);
|
|
if (!preamble)
|
|
VbExError("Error creating preamble.\n");
|
|
|
|
if (key_block_size + preamble->preamble_size > vblock_size)
|
|
VbExError("VBLOCK_A is too small for digest (%d bytes, needs %d)\n",
|
|
vblock_size, key_block_size + preamble->preamble_size);
|
|
|
|
memcpy(vblock_data, key_block, key_block_size);
|
|
memcpy(vblock_data + key_block_size, preamble, preamble->preamble_size);
|
|
|
|
free(body_digest);
|
|
free(preamble);
|
|
|
|
|
|
/* Sign FW B - skip if there isn't one */
|
|
if (!FindInFmap(fmap, "FW_MAIN_B", image, image_size, &fv_data, &fv_size) ||
|
|
!FindInFmap(fmap, "VBLOCK_B", image, image_size,
|
|
&vblock_data, &vblock_size)) {
|
|
printf("Image does not contain FW B - ignoring that part\n");
|
|
} else {
|
|
fv_size = FindImageEnd(fv_data, fv_size);
|
|
|
|
body_digest = CalculateHash(fv_data, fv_size, privkey);
|
|
if (!body_digest)
|
|
VbExError("Error calculating body digest\n");
|
|
|
|
preamble = CreateECPreamble(version, body_digest, privkey,
|
|
preamble_flags, name);
|
|
if (!preamble)
|
|
VbExError("Error creating preamble.\n");
|
|
|
|
if (key_block_size + preamble->preamble_size > vblock_size)
|
|
VbExError("VBLOCK_B is too small for digest (%d bytes, needs %d)\n",
|
|
vblock_size, key_block_size + preamble->preamble_size);
|
|
|
|
memcpy(vblock_data, key_block, key_block_size);
|
|
memcpy(vblock_data + key_block_size, preamble, preamble->preamble_size);
|
|
|
|
free(body_digest);
|
|
free(preamble);
|
|
}
|
|
|
|
/* Unmap to write changes to disk. */
|
|
if (0 != munmap(image, sb.st_size))
|
|
VbExError("Can't munmap %s: %s\n", filename, strerror(errno));
|
|
|
|
printf("Image signing completed\n");
|
|
|
|
}
|
|
|
|
static int Verify(const char *filename) {
|
|
struct stat sb;
|
|
int fd;
|
|
void *image;
|
|
uint64_t image_size;
|
|
FmapHeader* fmap;
|
|
VbECPreambleHeader *preamble;
|
|
VbPublicKey *pubkey;
|
|
uint64_t pubkey_size;
|
|
VbKeyBlockHeader *key_block;
|
|
uint64_t key_block_size;
|
|
uint8_t *fv_data = 0;
|
|
uint64_t fv_size;
|
|
VbPublicKey *data_key;
|
|
RSAPublicKey* rsa;
|
|
int errorcnt = 0;
|
|
char buf[80];
|
|
int i;
|
|
|
|
if (0 != stat(filename, &sb))
|
|
VbExError("Can't stat %s: %s\n", filename, strerror(errno));
|
|
|
|
fd = open(filename, O_RDONLY);
|
|
if (fd < 0)
|
|
VbExError("Can't open %s: %s\n", filename, strerror(errno));
|
|
|
|
image = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
if (image == (void *)-1)
|
|
VbExError("Can't mmap %s: %s\n", filename, strerror(errno));
|
|
close(fd); /* done with this now */
|
|
|
|
fmap = (FmapHeader *)FmapFind(image, sb.st_size);
|
|
if (!fmap)
|
|
VbExError("File %s doesn't have an FMAP - can't continue.\n");
|
|
|
|
if (fmap->fmap_size > sb.st_size)
|
|
VbExError("FMAP is bigger than file size (%ld vs %ld)\n",
|
|
fmap->fmap_size, sb.st_size);
|
|
|
|
image_size = sb.st_size;
|
|
|
|
/* Read pubkey */
|
|
if (!FindInFmap(fmap, "ROOT_KEY", image, image_size,
|
|
(uint8_t **)&pubkey, &pubkey_size)) {
|
|
printf("Can't find ROOT_KEY in %s\n", filename);
|
|
errorcnt++;
|
|
} else if (!GoodKey(pubkey, pubkey_size)) {
|
|
printf("ROOT_KEY is invalid\n");
|
|
errorcnt++;
|
|
} else {
|
|
printf("ROOT_KEY\n");
|
|
printf(" Algorithm: %" PRIu64 " %s\n", pubkey->algorithm,
|
|
(pubkey->algorithm < kNumAlgorithms ?
|
|
algo_strings[pubkey->algorithm] : "(invalid)"));
|
|
printf(" Key Version: %" PRIu64 "\n", pubkey->key_version);
|
|
printf(" Key sha1sum: ");
|
|
PrintPubKeySha1Sum(pubkey);
|
|
printf("\n");
|
|
}
|
|
|
|
for (i = 'A'; i <= 'B'; i++) {
|
|
|
|
fv_data = 0;
|
|
key_block = 0;
|
|
preamble = 0;
|
|
|
|
printf("FW %c\n", i);
|
|
sprintf(buf, "FW_MAIN_%c", i);
|
|
if (!FindInFmap(fmap, buf, image, image_size, &fv_data, &fv_size)) {
|
|
printf("Can't find %s in %s\n", buf, filename);
|
|
/* Not an error for firmware B */
|
|
if (i != 'B')
|
|
errorcnt++;
|
|
continue;
|
|
}
|
|
|
|
sprintf(buf, "VBLOCK_%c", i);
|
|
if (!FindInFmap(fmap, buf, image, image_size,
|
|
(uint8_t **)&key_block, &key_block_size)) {
|
|
printf("Can't find %s in %s\n", buf, filename);
|
|
/* Not an error for firmware B */
|
|
if (i != 'B')
|
|
errorcnt++;
|
|
continue;
|
|
}
|
|
|
|
if (0 != KeyBlockVerify(key_block, key_block_size, pubkey, !pubkey)) {
|
|
printf("Error verifying key block for %s.\n", buf);
|
|
errorcnt++;
|
|
continue;
|
|
}
|
|
printf(" Key block:\n");
|
|
data_key = &key_block->data_key;
|
|
printf(" Size: %" PRIu64 "\n",
|
|
key_block->key_block_size);
|
|
printf(" Flags: %" PRIu64 " (ignored)\n",
|
|
key_block->key_block_flags);
|
|
printf(" Data key algorithm: %" PRIu64 " %s\n", data_key->algorithm,
|
|
(data_key->algorithm < kNumAlgorithms ?
|
|
algo_strings[data_key->algorithm] : "(invalid)"));
|
|
printf(" Data key version: %" PRIu64 "\n", data_key->key_version);
|
|
printf(" Data key sha1sum: ");
|
|
PrintPubKeySha1Sum(data_key);
|
|
printf("\n");
|
|
|
|
preamble = (VbECPreambleHeader*)
|
|
((uint8_t *)key_block + key_block->key_block_size);
|
|
|
|
rsa = PublicKeyToRSA(&key_block->data_key);
|
|
if (!rsa) {
|
|
printf("Error parsing data key.\n");
|
|
errorcnt++;
|
|
}
|
|
/* Verify preamble */
|
|
if (0 != VerifyECPreamble(preamble,
|
|
key_block_size - key_block->key_block_size,
|
|
rsa)) {
|
|
printf("Error verifying preamble.\n");
|
|
errorcnt++;
|
|
free(rsa);
|
|
continue;
|
|
}
|
|
printf(" Preamble:\n");
|
|
printf(" Size: %" PRIu64 "\n",
|
|
preamble->preamble_size);
|
|
printf(" Header version: %" PRIu32 ".%" PRIu32"\n",
|
|
preamble->header_version_major,
|
|
preamble->header_version_minor);
|
|
printf(" Firmware version: %" PRIu64 "\n",
|
|
preamble->firmware_version);
|
|
printf(" Firmware body size: %" PRIu64 "\n",
|
|
preamble->body_digest.data_size);
|
|
printf(" Preamble flags: %" PRIu32 "\n", preamble->flags);
|
|
printf(" Preamble name: %s\n", preamble->name);
|
|
|
|
/* TODO: verify body size same as signature size */
|
|
|
|
/* Verify body */
|
|
if (preamble->flags & VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL) {
|
|
printf("Preamble requests USE_RO_NORMAL; skipping verification.\n");
|
|
} else {
|
|
if (0 != EqualData(fv_data, fv_size,
|
|
&preamble->body_digest, rsa)) {
|
|
printf("Error verifying firmware body.\n");
|
|
errorcnt++;
|
|
}
|
|
}
|
|
free(rsa);
|
|
}
|
|
|
|
/* Done */
|
|
if (0 != munmap(image, sb.st_size))
|
|
VbExError("Can't munmap %s: %s\n", filename, strerror(errno));
|
|
|
|
printf("Done\n");
|
|
return errorcnt;
|
|
}
|
|
|
|
int main(int argc, char* argv[]) {
|
|
|
|
char* filename = NULL;
|
|
uint64_t version = 0;
|
|
int got_version = 0;
|
|
uint32_t preamble_flags = 0;
|
|
char *name = NULL;
|
|
int mode = 0;
|
|
VbKeyBlockHeader* key_block = 0;
|
|
VbPrivateKey* privkey = 0;
|
|
VbPublicKey* pubkey = 0;
|
|
uint64_t key_block_size;
|
|
int errorcnt = 0;
|
|
char* e;
|
|
int i;
|
|
|
|
while ((i = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
|
|
switch (i) {
|
|
case '?':
|
|
/* Unhandled option */
|
|
printf("Unknown option\n");
|
|
errorcnt++;
|
|
break;
|
|
|
|
case OPT_MODE_SIGN:
|
|
case OPT_MODE_VERIFY:
|
|
mode = i;
|
|
filename = optarg;
|
|
break;
|
|
|
|
case OPT_KEYBLOCK:
|
|
/* Read the key block and keys */
|
|
key_block = (VbKeyBlockHeader*)ReadFile(optarg, &key_block_size);
|
|
if (!key_block) {
|
|
printf("Error reading key block from %s\n", optarg);
|
|
errorcnt++;
|
|
}
|
|
break;
|
|
|
|
case OPT_SIGNPUBKEY:
|
|
pubkey = PublicKeyRead(optarg);
|
|
if (!pubkey) {
|
|
printf("Error reading public key from %s\n", optarg);
|
|
errorcnt++;
|
|
}
|
|
break;
|
|
|
|
case OPT_SIGNPRIVATE:
|
|
privkey = PrivateKeyRead(optarg);
|
|
if (!privkey) {
|
|
printf("Error reading private key from %s\n", optarg);
|
|
errorcnt++;
|
|
}
|
|
break;
|
|
|
|
case OPT_VERSION:
|
|
version = strtoul(optarg, &e, 0);
|
|
if (!*optarg || (e && *e)) {
|
|
printf("Invalid --version argument: \"%s\"\n", optarg);
|
|
errorcnt++;
|
|
}
|
|
got_version = 1;
|
|
break;
|
|
|
|
case OPT_FLAGS:
|
|
preamble_flags = strtoul(optarg, &e, 0);
|
|
if (!*optarg || (e && *e)) {
|
|
printf("Invalid --flags argument: \"%s\"\n", optarg);
|
|
errorcnt++;
|
|
}
|
|
break;
|
|
|
|
case OPT_NAME:
|
|
name = optarg;
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch(mode) {
|
|
|
|
case OPT_MODE_SIGN:
|
|
/* Check required args */
|
|
if (!key_block) {
|
|
printf("The ----keyblock arg is required when signing\n");
|
|
errorcnt++;
|
|
}
|
|
if (!privkey) {
|
|
printf("The --signprivate arg is required when signing\n");
|
|
errorcnt++;
|
|
}
|
|
if (!got_version) {
|
|
printf("The --version arg is required when signing\n");
|
|
errorcnt++;
|
|
}
|
|
|
|
if (errorcnt)
|
|
return PrintHelp();
|
|
|
|
/* Sign or die */
|
|
SignImage(filename, key_block, key_block_size,
|
|
privkey, version, pubkey, preamble_flags, name);
|
|
|
|
/* fall through and verify what we've just done */
|
|
|
|
case OPT_MODE_VERIFY:
|
|
return Verify(filename);
|
|
|
|
default:
|
|
printf("\nMust specify a mode, either --sign or --verify.\n\n");
|
|
return PrintHelp();
|
|
}
|
|
}
|