diff --git a/tests/run_vbutil_tests.sh b/tests/run_vbutil_tests.sh index 0e8beef8d9..f3e759403d 100755 --- a/tests/run_vbutil_tests.sh +++ b/tests/run_vbutil_tests.sh @@ -69,7 +69,7 @@ ${datahashalgo}${COL_STOP}" # Pack ${UTIL_DIR}/vbutil_keyblock --pack ${keyblockfile} \ --datapubkey \ - tests/testkeys/key_alg${data_algorithmcounter}.vbpubk \ + ${TESTKEY_DIR}/key_alg${data_algorithmcounter}.vbpubk \ --signprivate ${TESTKEY_DIR}/key_rsa${signing_keylen}.pem \ --algorithm $signing_algorithmcounter if [ $? -ne 0 ] @@ -80,7 +80,7 @@ ${datahashalgo}${COL_STOP}" # Unpack ${UTIL_DIR}/vbutil_keyblock --unpack ${keyblockfile} \ --signpubkey \ - tests/testkeys/key_alg${signing_algorithmcounter}.vbpubk + ${TESTKEY_DIR}/key_alg${signing_algorithmcounter}.vbpubk # TODO: check data key against the packed one? if [ $? -ne 0 ] then diff --git a/utility/Makefile b/utility/Makefile index 697cc9ec2a..23c0b65dbf 100644 --- a/utility/Makefile +++ b/utility/Makefile @@ -32,6 +32,7 @@ TARGET_BINS = dumpRSAPublicKey \ load_kernel_test \ signature_digest_utility \ vbutil_key \ + vbutil_kernel \ vbutil_keyblock \ verify_data @@ -57,6 +58,9 @@ kernel_utility: kernel_utility.cc $(LIBS) signature_digest_utility: signature_digest_utility.c $(LIBS) $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) -lcrypto +vbutil_kernel: vbutil_kernel.c $(LIBS) + $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) -lcrypto + vbutil_key: vbutil_key.c $(LIBS) $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) -lcrypto diff --git a/vkernel/include/kernel_blob.h b/utility/include/kernel_blob.h similarity index 100% rename from vkernel/include/kernel_blob.h rename to utility/include/kernel_blob.h diff --git a/utility/vbutil_kernel.c b/utility/vbutil_kernel.c new file mode 100644 index 0000000000..32f844c612 --- /dev/null +++ b/utility/vbutil_kernel.c @@ -0,0 +1,465 @@ +/* Copyright (c) 2010 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 kernel utility + */ + +#include +#include /* For PRIu64 */ +#include +#include +#include +#include + +#include "cryptolib.h" +#include "host_common.h" +#include "kernel_blob.h" +#include "vboot_common.h" + + +/* Command line options */ +enum { + OPT_MODE_PACK = 1000, + OPT_MODE_VERIFY, + OPT_KEYBLOCK, + OPT_SIGNPUBKEY, + OPT_SIGNPRIVATE, + OPT_VERSION, + OPT_VMLINUZ, + OPT_BOOTLOADER, + OPT_CONFIG, + OPT_PAD, +}; + +static struct option long_opts[] = { + {"pack", 1, 0, OPT_MODE_PACK }, + {"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 }, + {"vmlinuz", 1, 0, OPT_VMLINUZ }, + {"bootloader", 1, 0, OPT_BOOTLOADER }, + {"config", 1, 0, OPT_CONFIG }, + {"pad", 1, 0, OPT_PAD }, + {NULL, 0, 0, 0} +}; + + +/* Print help and return error */ +static int PrintHelp(void) { + + puts("vbutil_kernel - Verified boot key block utility\n" + "\n" + "Usage: vbutil_kernel <--pack|--verify> [OPTIONS]\n" + "\n" + "For '--pack ', required OPTIONS are:\n" + " --keyblock Key block in .keyblock format\n" + " --signprivate Signing private key in .pem format\n" + " --version Kernel version\n" + " --vmlinuz Linux kernel image\n" + " --bootloader Bootloader stub\n" + " --config Config file\n" + "Optional OPTIONS are:\n" + " --pad Padding size in bytes\n" + "\n" + "For '--verify ', required OPTIONS are:\n" + " --signpubkey Signing public key in .vbpubk format\n" + ""); + return 1; +} + + +/* Return the smallest integral multiple of [alignment] that is equal + * to or greater than [val]. Used to determine the number of + * pages/sectors/blocks/whatever needed to contain [val] + * items/bytes/etc. */ +static uint64_t roundup(uint64_t val, uint64_t alignment) { + uint64_t rem = val % alignment; + if ( rem ) + return val + (alignment - rem); + return val; +} + + +/* Match regexp /\b--\b/ to delimit the start of the kernel commandline. If we + * don't find one, we'll use the whole thing. */ +static unsigned int find_cmdline_start(char *input, unsigned int max_len) { + int start = 0; + int i; + for(i = 0; i < max_len - 1 && input[i]; i++) { + if ('-' == input[i] && '-' == input[i + 1]) { /* found a "--" */ + if ((i == 0 || ' ' == input[i - 1]) && /* nothing before it */ + (i + 2 >= max_len || ' ' == input[i+2])) { /* nothing after it */ + start = i+2; /* note: hope there's a trailing '\0' */ + break; + } + } + } + while(' ' == input[start]) /* skip leading spaces */ + start++; + + return start; +} + + +/* Pack a .kernel */ +static int Pack(const char* outfile, const char* keyblock_file, + const char* signprivate, uint64_t version, + const char* vmlinuz, const char* bootloader_file, + const char* config_file, uint64_t pad) { + + struct linux_kernel_header *lh = 0; + struct linux_kernel_params *params = 0; + VbPrivateKey* signing_key; + VbSignature* body_sig; + VbKernelPreambleHeader* preamble; + VbKeyBlockHeader* key_block; + uint64_t key_block_size; + uint8_t* config_buf; + uint64_t config_size; + uint8_t* bootloader_buf; + uint64_t bootloader_size; + uint64_t bootloader_mem_start; + uint64_t bootloader_mem_size; + uint8_t* kernel_buf; + uint64_t kernel_size; + uint64_t kernel32_start = 0; + uint64_t kernel32_size = 0; + uint32_t cmdline_addr; + uint8_t* blob = NULL; + uint64_t blob_size; + uint64_t now = 0; + FILE* f; + uint64_t i; + + if (!outfile) { + error("Must specify output filename\n"); + return 1; + } + if (!keyblock_file || !signprivate) { + error("Must specify all keys\n"); + return 1; + } + if (!vmlinuz || !bootloader_file || !config_file) { + error("Must specify all input files\n"); + return 1; + } + + /* Read the key block and private key */ + key_block = (VbKeyBlockHeader*)ReadFile(keyblock_file, &key_block_size); + if (!key_block) { + error("Error reading key block.\n"); + return 1; + } + if (pad < key_block->key_block_size) { + error("Pad too small\n"); + return 1; + } + + signing_key = PrivateKeyRead(signprivate, key_block->data_key.algorithm); + if (!signing_key) { + error("Error reading signing key.\n"); + return 1; + } + + /* Read the config file */ + config_buf = ReadFile(config_file, &config_size); + if (!config_buf) + return 1; + if (CROS_CONFIG_SIZE <= config_size) { /* need room for trailing '\0' */ + error("Config file %s is too large (>= %d bytes)\n", + config_file, CROS_CONFIG_SIZE); + return 1; + } + /* Replace newlines with spaces */ + for (i = 0; i < config_size; i++) + if ('\n' == config_buf[i]) + config_buf[i] = ' '; + + /* Read the bootloader */ + bootloader_buf = ReadFile(bootloader_file, &bootloader_size); + if (!bootloader_buf) + return 1; + + /* Read the kernel */ + kernel_buf = ReadFile(vmlinuz, &kernel_size); + if (!kernel_buf) + return 1; + if (!kernel_size) { + error("Empty kernel file\n"); + return 1; + } + + /* The first part of vmlinuz is a header, followed by a real-mode + * boot stub. We only want the 32-bit part. */ + lh = (struct linux_kernel_header *)kernel_buf; + kernel32_start = (lh->setup_sects + 1) << 9; + if (kernel32_start >= kernel_size) { + error("Malformed kernel\n"); + return 1; + } + kernel32_size = kernel_size - kernel32_start; + + /* Allocate and zero the blob we need. */ + blob_size = roundup(kernel32_size, CROS_ALIGN) + + CROS_CONFIG_SIZE + + CROS_PARAMS_SIZE + + roundup(bootloader_size, CROS_ALIGN); + blob = (uint8_t *)Malloc(blob_size); + if (!blob) { + error("Couldn't allocate %ld bytes.\n", blob_size); + return 1; + } + Memset(blob, 0, blob_size); + + /* Copy the 32-bit kernel. */ + if (kernel32_size) + Memcpy(blob + now, kernel_buf + kernel32_start, kernel32_size); + now += roundup(now + kernel32_size, CROS_ALIGN); + + /* Find the load address of the commandline. We'll need it later. */ + cmdline_addr = CROS_32BIT_ENTRY_ADDR + now + + find_cmdline_start((char *)config_buf, config_size); + + /* Copy the config. */ + if (config_size) + Memcpy(blob + now, config_buf, config_size); + now += CROS_CONFIG_SIZE; + + /* The zeropage data is next. Overlay the linux_kernel_header onto it, and + * tweak a few fields. */ + params = (struct linux_kernel_params *)(blob + now); + Memcpy(&(params->setup_sects), &(lh->setup_sects), + sizeof(*lh) - offsetof(struct linux_kernel_header, setup_sects)); + params->boot_flag = 0; + params->ramdisk_image = 0; /* we don't support initrd */ + params->ramdisk_size = 0; + params->type_of_loader = 0xff; + params->cmd_line_ptr = cmdline_addr; + now += CROS_PARAMS_SIZE; + + /* Finally, append the bootloader. Remember where it will load in + * memory, too. */ + bootloader_mem_start = CROS_32BIT_ENTRY_ADDR + now; + bootloader_mem_size = roundup(bootloader_size, CROS_ALIGN); + if (bootloader_size) + Memcpy(blob + now, bootloader_buf, bootloader_size); + now += bootloader_mem_size; + + /* Free input buffers */ + Free(kernel_buf); + Free(config_buf); + Free(bootloader_buf); + + /* Sign the kernel data */ + body_sig = CalculateSignature(blob, blob_size, signing_key); + if (!body_sig) { + error("Error calculating body signature\n"); + return 1; + } + + /* Create preamble */ + preamble = CreateKernelPreamble(version, + CROS_32BIT_ENTRY_ADDR, + bootloader_mem_start, + bootloader_mem_size, + body_sig, + pad - key_block_size, + signing_key); + if (!preamble) { + error("Error creating preamble.\n"); + return 1; + } + + /* Write the output file */ + f = fopen(outfile, "wb"); + if (!f) { + error("Can't open output file %s\n", outfile); + return 1; + } + i = ((1 != fwrite(key_block, key_block_size, 1, f)) || + (1 != fwrite(preamble, preamble->preamble_size, 1, f)) || + (1 != fwrite(blob, blob_size, 1, f))); + fclose(f); + if (i) { + error("Can't write output file %s\n", outfile); + unlink(outfile); + return 1; + } + + /* Success */ + return 0; +} + + +static int Verify(const char* infile, const char* signpubkey) { + + VbKeyBlockHeader* key_block; + VbKernelPreambleHeader* preamble; + VbPublicKey* data_key; + VbPublicKey* sign_key; + RSAPublicKey* rsa; + uint8_t* blob; + uint64_t blob_size; + uint64_t now = 0; + + if (!infile || !signpubkey) { + error("Must specify filename and signpubkey\n"); + return 1; + } + + /* Read public signing key */ + sign_key = PublicKeyRead(signpubkey); + if (!sign_key) { + error("Error reading signpubkey.\n"); + return 1; + } + + /* Read blob */ + blob = ReadFile(infile, &blob_size); + if (!blob) { + error("Error reading input file\n"); + return 1; + } + + /* Verify key block */ + key_block = (VbKeyBlockHeader*)blob; + if (0 != VerifyKeyBlock(key_block, blob_size, sign_key)) { + error("Error verifying key block.\n"); + return 1; + } + Free(sign_key); + now += key_block->key_block_size; + + printf("Key block:\n"); + data_key = &key_block->data_key; + printf(" Size: %" PRIu64 "\n", key_block->key_block_size); + 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(" Flags: %" PRIu64 "\n", key_block->key_block_flags); + + rsa = PublicKeyToRSA(&key_block->data_key); + if (!rsa) { + error("Error parsing data key.\n"); + return 1; + } + + /* Verify preamble */ + preamble = (VbKernelPreambleHeader*)(blob + now); + if (0 != VerifyKernelPreamble2(preamble, blob_size - now, rsa)) { + error("Error verifying preamble.\n"); + return 1; + } + now += preamble->preamble_size; + + 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(" Kernel version: %" PRIu64 "\n", preamble->kernel_version); + printf(" Body load address: %" PRIu64 "\n", preamble->body_load_address); + printf(" Body size: %" PRIu64 "\n", + preamble->body_signature.data_size); + printf(" Bootloader address: %" PRIu64 "\n", preamble->bootloader_address); + printf(" Bootloader size: %" PRIu64 "\n", preamble->bootloader_size); + + /* Verify body */ + if (0 != VerifyData(blob + now, &preamble->body_signature, rsa)) { + error("Error verifying kernel body.\n"); + return 1; + } + printf("Body verification succeeded.\n"); + return 0; +} + + +int main(int argc, char* argv[]) { + + char* filename = NULL; + char* key_block_file = NULL; + char* signpubkey = NULL; + char* signprivate = NULL; + uint64_t version = 0; + char* vmlinuz = NULL; + char* bootloader = NULL; + char* config_file = NULL; + uint64_t pad = 65536; + int mode = 0; + int parse_error = 0; + char* e; + int i; + + while ((i = getopt_long(argc, argv, "", long_opts, NULL)) != -1) { + switch (i) { + case '?': + /* Unhandled option */ + printf("Unknown option\n"); + parse_error = 1; + break; + + case OPT_MODE_PACK: + case OPT_MODE_VERIFY: + mode = i; + filename = optarg; + break; + + case OPT_KEYBLOCK: + key_block_file = optarg; + break; + + case OPT_SIGNPUBKEY: + signpubkey = optarg; + break; + + case OPT_SIGNPRIVATE: + signprivate = optarg; + break; + + case OPT_VMLINUZ: + vmlinuz = optarg; + break; + + case OPT_BOOTLOADER: + bootloader = optarg; + break; + + case OPT_CONFIG: + config_file = optarg; + break; + + case OPT_VERSION: + version = strtoul(optarg, &e, 0); + if (!*optarg || (e && *e)) { + printf("Invalid --version\n"); + parse_error = 1; + } + break; + + case OPT_PAD: + pad = strtoul(optarg, &e, 0); + if (!*optarg || (e && *e)) { + printf("Invalid --pad\n"); + parse_error = 1; + } + break; + } + } + + if (parse_error) + return PrintHelp(); + + switch(mode) { + case OPT_MODE_PACK: + return Pack(filename, key_block_file, signprivate, version, vmlinuz, + bootloader, config_file, pad); + case OPT_MODE_VERIFY: + return Verify(filename, signpubkey); + default: + printf("Must specify a mode.\n"); + return PrintHelp(); + } +} diff --git a/utility/vbutil_key.c b/utility/vbutil_key.c index d770026ecd..6f9dc23366 100644 --- a/utility/vbutil_key.c +++ b/utility/vbutil_key.c @@ -1,3 +1,10 @@ +/* Copyright (c) 2010 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 key utility + */ + #include #include /* For PRIu64 */ #include diff --git a/utility/vbutil_keyblock.c b/utility/vbutil_keyblock.c index 37754c9239..860a07cebb 100644 --- a/utility/vbutil_keyblock.c +++ b/utility/vbutil_keyblock.c @@ -1,3 +1,10 @@ +/* Copyright (c) 2010 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 key block utility + */ + #include #include /* For PRIu64 */ #include diff --git a/vkernel/Makefile b/vkernel/Makefile index 827a53ed6b..1a9fa0793f 100644 --- a/vkernel/Makefile +++ b/vkernel/Makefile @@ -7,6 +7,7 @@ INCLUDES += -I./include \ -I$(FWDIR)/lib/include \ -I$(FWDIR)/lib/cryptolib/include \ -I../common/include \ + -I../utility/include \ -I../misclibs/include CFLAGS ?= -Wall -DNDEBUG -O3 -Werror KERNEL_OUT = kernel_image.o