| /* 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 <errno.h> |
| #include <getopt.h> |
| #include <inttypes.h> /* For PRIu64 */ |
| #include <stdarg.h> |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include "cryptolib.h" |
| #include "host_common.h" |
| #include "kernel_blob.h" |
| #include "vboot_common.h" |
| |
| |
| /* Global opt */ |
| static int opt_debug = 0; |
| |
| static const int DEFAULT_PADDING = 65536; |
| |
| /* Command line options */ |
| enum { |
| OPT_MODE_PACK = 1000, |
| OPT_MODE_REPACK, |
| OPT_MODE_VERIFY, |
| OPT_OLDBLOB, |
| OPT_KEYBLOCK, |
| OPT_SIGNPUBKEY, |
| OPT_SIGNPRIVATE, |
| OPT_VERSION, |
| OPT_VMLINUZ, |
| OPT_BOOTLOADER, |
| OPT_CONFIG, |
| OPT_VBLOCKONLY, |
| OPT_PAD, |
| OPT_VERBOSE, |
| }; |
| |
| static struct option long_opts[] = { |
| {"pack", 1, 0, OPT_MODE_PACK }, |
| {"repack", 1, 0, OPT_MODE_REPACK }, |
| {"verify", 1, 0, OPT_MODE_VERIFY }, |
| {"oldblob", 1, 0, OPT_OLDBLOB }, |
| {"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 }, |
| {"vblockonly", 0, 0, OPT_VBLOCKONLY }, |
| {"pad", 1, 0, OPT_PAD }, |
| {"verbose", 0, 0, OPT_VERBOSE }, |
| {"debug", 0, &opt_debug, 1 }, |
| {NULL, 0, 0, 0} |
| }; |
| |
| |
| /* Print help and return error */ |
| static int PrintHelp(char *progname) { |
| fprintf(stderr, |
| "This program creates, signs, and verifies the kernel blob\n"); |
| fprintf(stderr, |
| "\n" |
| "Usage: %s --pack <file> [PARAMETERS]\n" |
| "\n" |
| " Required parameters:\n" |
| " --keyblock <file> Key block in .keyblock format\n" |
| " --signprivate <file>" |
| " Private key to sign kernel data, in .vbprivk format\n" |
| " --version <number> Kernel version\n" |
| " --vmlinuz <file> Linux kernel bzImage file\n" |
| " --bootloader <file> Bootloader stub\n" |
| " --config <file> Command line file\n" |
| "\n" |
| " Optional:\n" |
| " --pad <number> Verification padding size in bytes\n" |
| " --vblockonly Emit just the verification blob\n", |
| progname); |
| fprintf(stderr, |
| "\nOR\n\n" |
| "Usage: %s --repack <file> [PARAMETERS]\n" |
| "\n" |
| " Required parameters (of --keyblock, --config, and --version \n" |
| " at least one is required):\n" |
| " --keyblock <file> Key block in .keyblock format\n" |
| " --signprivate <file>" |
| " Private key to sign kernel data, in .vbprivk format\n" |
| " --oldblob <file> Previously packed kernel blob\n" |
| " --config <file> New command line file\n" |
| " --version <number> Kernel version\n" |
| "\n" |
| " Optional:\n" |
| " --pad <number> Verification padding size in bytes\n" |
| " --vblockonly Emit just the verification blob\n", |
| progname); |
| fprintf(stderr, |
| "\nOR\n\n" |
| "Usage: %s --verify <file> [PARAMETERS]\n" |
| "\n" |
| " Optional:\n" |
| " --signpubkey <file>" |
| " Public key to verify kernel keyblock, in .vbpubk format\n" |
| " --verbose Print a more detailed report\n" |
| "\n", |
| progname); |
| return 1; |
| } |
| |
| static void Debug(const char *format, ...) { |
| if (!opt_debug) |
| return; |
| |
| va_list ap; |
| va_start(ap, format); |
| fprintf(stderr, "DEBUG: "); |
| vfprintf(stderr, format, ap); |
| va_end(ap); |
| } |
| |
| /* Return an explanation when fread() fails. */ |
| static const char *error_fread(FILE *fp) { |
| const char *retval = "beats me why"; |
| if (feof(fp)) |
| retval = "EOF"; |
| else if (ferror(fp)) |
| retval = strerror(errno); |
| clearerr(fp); |
| return retval; |
| } |
| |
| /* 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; |
| } |
| |
| |
| typedef struct blob_s { |
| /* Stuff needed by VbKernelPreambleHeader */ |
| uint64_t kernel_version; |
| uint64_t bootloader_address; |
| uint64_t bootloader_size; |
| /* Raw kernel blob data */ |
| uint64_t blob_size; |
| uint8_t *blob; |
| |
| /* these fields are not always initialized */ |
| VbKernelPreambleHeader* preamble; |
| VbKeyBlockHeader* key_block; |
| uint8_t *buf; |
| |
| } blob_t; |
| |
| /* Given a blob return the location of the kernel command line buffer. */ |
| static char* BpCmdLineLocation(blob_t *bp) |
| { |
| return (char*)(bp->blob + bp->bootloader_address - CROS_32BIT_ENTRY_ADDR - |
| CROS_CONFIG_SIZE - CROS_PARAMS_SIZE); |
| } |
| |
| static void FreeBlob(blob_t *bp) { |
| if (bp) { |
| if (bp->blob) |
| Free(bp->blob); |
| if (bp->buf) |
| Free(bp->buf); |
| Free(bp); |
| } |
| } |
| |
| /* |
| * Read the kernel command line from a file. Get rid of \n characters along |
| * the way and verify that the line fits into a 4K buffer. |
| * |
| * Return the buffer contaning the line on success (and set the line length |
| * using the passed in parameter), or NULL in case something goes wrong. |
| */ |
| static uint8_t* ReadConfigFile(const char* config_file, uint64_t* config_size) |
| { |
| uint8_t* config_buf; |
| int ii; |
| |
| config_buf = ReadFile(config_file, config_size); |
| Debug(" config file size=0x%" PRIx64 "\n", *config_size); |
| 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 NULL; |
| } |
| |
| /* Replace newlines with spaces */ |
| for (ii = 0; ii < *config_size; ii++) { |
| if ('\n' == config_buf[ii]) { |
| config_buf[ii] = ' '; |
| } |
| } |
| return config_buf; |
| } |
| |
| /* Create a blob from its components */ |
| static blob_t *NewBlob(uint64_t version, |
| const char* vmlinuz, |
| const char* bootloader_file, |
| const char* config_file) { |
| blob_t *bp; |
| struct linux_kernel_header *lh = 0; |
| struct linux_kernel_params *params = 0; |
| uint8_t* config_buf; |
| uint64_t config_size; |
| uint8_t* bootloader_buf; |
| uint64_t bootloader_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 now = 0; |
| |
| if (!vmlinuz || !bootloader_file || !config_file) { |
| error("Must specify all input files\n"); |
| return 0; |
| } |
| |
| bp = (blob_t *)Malloc(sizeof(blob_t)); |
| if (!bp) { |
| error("Couldn't allocate bytes for blob_t.\n"); |
| return 0; |
| } |
| |
| Memset(bp, 0, sizeof(*bp)); |
| bp->kernel_version = version; |
| |
| /* Read the config file */ |
| Debug("Reading %s\n", config_file); |
| config_buf = ReadConfigFile(config_file, &config_size); |
| if (!config_buf) |
| return 0; |
| |
| /* Read the bootloader */ |
| Debug("Reading %s\n", bootloader_file); |
| bootloader_buf = ReadFile(bootloader_file, &bootloader_size); |
| if (!bootloader_buf) |
| return 0; |
| Debug(" bootloader file size=0x%" PRIx64 "\n", bootloader_size); |
| |
| /* Read the kernel */ |
| Debug("Reading %s\n", vmlinuz); |
| kernel_buf = ReadFile(vmlinuz, &kernel_size); |
| if (!kernel_buf) |
| return 0; |
| Debug(" kernel file size=0x%" PRIx64 "\n", kernel_size); |
| if (!kernel_size) { |
| error("Empty kernel file\n"); |
| return 0; |
| } |
| |
| /* 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 0; |
| } |
| kernel32_size = kernel_size - kernel32_start; |
| Debug(" kernel32_start=0x%" PRIx64 "\n", kernel32_start); |
| Debug(" kernel32_size=0x%" PRIx64 "\n", kernel32_size); |
| |
| /* Allocate and zero the blob we need. */ |
| bp->blob_size = roundup(kernel32_size, CROS_ALIGN) + |
| CROS_CONFIG_SIZE + |
| CROS_PARAMS_SIZE + |
| roundup(bootloader_size, CROS_ALIGN); |
| blob = (uint8_t *)Malloc(bp->blob_size); |
| Debug("blob_size=0x%" PRIx64 "\n", bp->blob_size); |
| if (!blob) { |
| error("Couldn't allocate %ld bytes.\n", bp->blob_size); |
| return 0; |
| } |
| Memset(blob, 0, bp->blob_size); |
| bp->blob = blob; |
| |
| /* Copy the 32-bit kernel. */ |
| Debug("kernel goes at blob+=0x%" PRIx64 "\n", now); |
| if (kernel32_size) |
| Memcpy(blob + now, kernel_buf + kernel32_start, kernel32_size); |
| now += roundup(now + kernel32_size, CROS_ALIGN); |
| |
| Debug("config goes at blob+0x%" PRIx64 "\n", now); |
| /* 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); |
| Debug(" cmdline_addr=0x%" PRIx64 "\n", cmdline_addr); |
| |
| /* 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. */ |
| Debug("params goes at blob+=0x%" PRIx64 "\n", now); |
| 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; |
| /* A fake e820 memory map with 2 entries */ |
| params->n_e820_entry = 2; |
| params->e820_entries[0].start_addr = 0x00000000; |
| params->e820_entries[0].segment_size = 0x00001000; |
| params->e820_entries[0].segment_type = E820_TYPE_RAM; |
| params->e820_entries[1].start_addr = 0xfffff000; |
| params->e820_entries[1].segment_size = 0x00001000; |
| params->e820_entries[1].segment_type = E820_TYPE_RESERVED; |
| now += CROS_PARAMS_SIZE; |
| |
| /* Finally, append the bootloader. Remember where it will load in |
| * memory, too. */ |
| Debug("bootloader goes at blob+=0x%" PRIx64 "\n", now); |
| bp->bootloader_address = CROS_32BIT_ENTRY_ADDR + now; |
| bp->bootloader_size = roundup(bootloader_size, CROS_ALIGN); |
| Debug(" bootloader_address=0x%" PRIx64 "\n", bp->bootloader_address); |
| Debug(" bootloader_size=0x%" PRIx64 "\n", bp->bootloader_size); |
| if (bootloader_size) |
| Memcpy(blob + now, bootloader_buf, bootloader_size); |
| now += bp->bootloader_size; |
| Debug("end of blob is 0x%" PRIx64 "\n", now); |
| |
| /* Free input buffers */ |
| Free(kernel_buf); |
| Free(config_buf); |
| Free(bootloader_buf); |
| |
| /* Success */ |
| return bp; |
| } |
| |
| |
| /* Pull the blob_t stuff out of a prepacked kernel blob file */ |
| static blob_t *OldBlob(const char* filename) { |
| FILE* fp = NULL; |
| blob_t *bp = NULL; |
| struct stat statbuf; |
| VbKeyBlockHeader* key_block; |
| VbKernelPreambleHeader* preamble; |
| uint64_t now = 0; |
| uint8_t* buf = NULL; |
| int ret_error = 1; |
| |
| if (!filename) { |
| error("Must specify prepacked blob to read\n"); |
| return 0; |
| } |
| |
| if (0 != stat(filename, &statbuf)) { |
| error("Unable to stat %s: %s\n", filename, strerror(errno)); |
| return 0; |
| } |
| |
| Debug("%s size is 0x%" PRIx64 "\n", filename, statbuf.st_size); |
| if (statbuf.st_size < DEFAULT_PADDING) { |
| error("%s is too small to be a valid kernel blob\n"); |
| return 0; |
| } |
| |
| Debug("Reading %s\n", filename); |
| fp = fopen(filename, "rb"); |
| if (!fp) { |
| error("Unable to open file %s: %s\n", filename, strerror(errno)); |
| return 0; |
| } |
| |
| buf = Malloc(DEFAULT_PADDING); |
| if (!buf) { |
| error("Unable to allocate padding\n"); |
| goto unwind_oldblob; |
| } |
| |
| if (1 != fread(buf, DEFAULT_PADDING, 1, fp)) { |
| error("Unable to read header from %s: %s\n", filename, error_fread(fp)); |
| goto unwind_oldblob; |
| } |
| |
| /* Skip the key block */ |
| key_block = (VbKeyBlockHeader*)buf; |
| Debug("Keyblock is 0x%" PRIx64 " bytes\n", key_block->key_block_size); |
| now += key_block->key_block_size; |
| if (now > statbuf.st_size) { |
| error("key_block_size advances past the end of the blob\n"); |
| goto unwind_oldblob; |
| } |
| |
| /* Skip the preamble */ |
| preamble = (VbKernelPreambleHeader*)(buf + now); |
| Debug("Preamble is 0x%" PRIx64 " bytes\n", preamble->preamble_size); |
| now += preamble->preamble_size; |
| if (now > statbuf.st_size) { |
| error("preamble_size advances past the end of the blob\n"); |
| goto unwind_oldblob; |
| } |
| |
| /* Go find the kernel blob */ |
| Debug("kernel blob is at offset 0x%" PRIx64 "\n", now); |
| if (0 != fseek(fp, now, SEEK_SET)) { |
| error("Unable to seek to 0x%" PRIx64 " in %s: %s\n", now, filename, |
| strerror(errno)); |
| goto unwind_oldblob; |
| } |
| |
| /* Remember what we've got */ |
| bp = (blob_t *)Malloc(sizeof(blob_t)); |
| if (!bp) { |
| error("Couldn't allocate bytes for blob_t.\n"); |
| goto unwind_oldblob; |
| } |
| |
| bp->buf = buf; |
| bp->key_block = key_block; |
| bp->preamble = preamble; |
| |
| bp->kernel_version = preamble->kernel_version; |
| bp->bootloader_address = preamble->bootloader_address; |
| bp->bootloader_size = preamble->bootloader_size; |
| bp->blob_size = preamble->body_signature.data_size; |
| |
| Debug(" kernel_version = %d\n", bp->kernel_version); |
| Debug(" bootloader_address = 0x%" PRIx64 "\n", bp->bootloader_address); |
| Debug(" bootloader_size = 0x%" PRIx64 "\n", bp->bootloader_size); |
| Debug(" blob_size = 0x%" PRIx64 "\n", bp->blob_size); |
| |
| if (!bp->blob_size) { |
| error("No kernel blob found\n"); |
| goto unwind_oldblob; |
| } |
| |
| bp->blob = (uint8_t *)Malloc(bp->blob_size); |
| if (!bp->blob) { |
| error("Couldn't allocate 0x%" PRIx64 " bytes for blob_t.\n", bp->blob_size); |
| goto unwind_oldblob; |
| } |
| |
| /* read it in */ |
| if (1 != fread(bp->blob, bp->blob_size, 1, fp)) { |
| error("Unable to read kernel blob from %s: %s\n", filename, error_fread(fp)); |
| goto unwind_oldblob; |
| } |
| |
| ret_error = 0; |
| |
| /* done */ |
| unwind_oldblob: |
| fclose(fp); |
| if (ret_error) { |
| if (bp) { |
| FreeBlob(bp); |
| bp = NULL; |
| } else if (buf) { |
| Free(buf); |
| } |
| } |
| return bp; |
| } |
| |
| |
| /* Pack a .kernel */ |
| static int Pack(const char* outfile, const char* keyblock_file, |
| const char* signprivate, blob_t *bp, uint64_t pad, |
| int vblockonly) { |
| VbPrivateKey* signing_key; |
| VbSignature* body_sig; |
| VbKernelPreambleHeader* preamble; |
| VbKeyBlockHeader* key_block; |
| uint64_t key_block_size; |
| FILE* f; |
| uint64_t i; |
| |
| if (!outfile) { |
| error("Must specify output filename\n"); |
| return 1; |
| } |
| if ((!keyblock_file && !bp->key_block) || !signprivate) { |
| error("Must specify all keys\n"); |
| return 1; |
| } |
| if (!bp) { |
| error("Refusing to pack invalid kernel blob\n"); |
| return 1; |
| } |
| |
| /* Get the key block and read the private key. */ |
| if (keyblock_file) { |
| key_block = (VbKeyBlockHeader*)ReadFile(keyblock_file, &key_block_size); |
| if (!key_block) { |
| error("Error reading key block.\n"); |
| return 1; |
| } |
| } else { |
| key_block = bp->key_block; |
| key_block_size = key_block->key_block_size; |
| } |
| |
| if (pad < key_block->key_block_size) { |
| error("Pad too small\n"); |
| return 1; |
| } |
| |
| signing_key = PrivateKeyRead(signprivate); |
| if (!signing_key) { |
| error("Error reading signing key.\n"); |
| return 1; |
| } |
| |
| /* Sign the kernel data */ |
| body_sig = CalculateSignature(bp->blob, bp->blob_size, signing_key); |
| if (!body_sig) { |
| error("Error calculating body signature\n"); |
| return 1; |
| } |
| |
| /* Create preamble */ |
| preamble = CreateKernelPreamble(bp->kernel_version, |
| CROS_32BIT_ENTRY_ADDR, |
| bp->bootloader_address, |
| bp->bootloader_size, |
| body_sig, |
| pad - key_block_size, |
| signing_key); |
| if (!preamble) { |
| error("Error creating preamble.\n"); |
| return 1; |
| } |
| |
| /* Write the output file */ |
| Debug("writing %s...\n", outfile); |
| f = fopen(outfile, "wb"); |
| if (!f) { |
| error("Can't open output file %s\n", outfile); |
| return 1; |
| } |
| Debug("0x%" PRIx64 " bytes of key_block\n", key_block_size); |
| Debug("0x%" PRIx64 " bytes of preamble\n", preamble->preamble_size); |
| i = ((1 != fwrite(key_block, key_block_size, 1, f)) || |
| (1 != fwrite(preamble, preamble->preamble_size, 1, f))); |
| if (i) { |
| error("Can't write output file %s\n", outfile); |
| fclose(f); |
| unlink(outfile); |
| return 1; |
| } |
| |
| if (!vblockonly) { |
| Debug("0x%" PRIx64 " bytes of blob\n", bp->blob_size); |
| i = (1 != fwrite(bp->blob, bp->blob_size, 1, f)); |
| if (i) { |
| error("Can't write output file %s\n", outfile); |
| fclose(f); |
| unlink(outfile); |
| return 1; |
| } |
| } |
| |
| fclose(f); |
| |
| /* Success */ |
| return 0; |
| } |
| |
| /* |
| * Replace kernel command line in a blob representing a kernel. |
| */ |
| static int ReplaceConfig(blob_t* bp, const char* config_file) |
| { |
| uint8_t* new_conf; |
| uint64_t config_size; |
| |
| if (!config_file) { |
| return 0; |
| } |
| |
| new_conf = ReadConfigFile(config_file, &config_size); |
| if (!new_conf) { |
| return 1; |
| } |
| |
| /* fill the config buffer with zeros */ |
| Memset(BpCmdLineLocation(bp), 0, CROS_CONFIG_SIZE); |
| Memcpy(BpCmdLineLocation(bp), new_conf, config_size); |
| Free(new_conf); |
| return 0; |
| } |
| |
| static int Verify(const char* infile, const char* signpubkey, int verbose) { |
| |
| VbKeyBlockHeader* key_block; |
| VbKernelPreambleHeader* preamble; |
| VbPublicKey* data_key; |
| VbPublicKey* sign_key = NULL; |
| RSAPublicKey* rsa; |
| blob_t* bp; |
| uint64_t now; |
| int rv = 1; |
| |
| if (!infile) { |
| error("Must specify filename\n"); |
| return 1; |
| } |
| |
| /* Read public signing key */ |
| if (signpubkey) { |
| sign_key = PublicKeyRead(signpubkey); |
| if (!sign_key) { |
| error("Error reading signpubkey.\n"); |
| return 1; |
| } |
| } |
| |
| /* Read blob */ |
| bp = OldBlob(infile); |
| if (!bp) { |
| error("Error reading input file\n"); |
| return 1; |
| } |
| |
| /* Verify key block */ |
| key_block = bp->key_block; |
| if (0 != KeyBlockVerify(key_block, bp->blob_size, sign_key, |
| (sign_key ? 0 : 1))) { |
| error("Error verifying key block.\n"); |
| goto verify_exit; |
| } |
| now = key_block->key_block_size; |
| |
| printf("Key block:\n"); |
| data_key = &key_block->data_key; |
| if (verbose) |
| printf(" Signature: %s\n", sign_key ? "valid" : "ignored"); |
| printf(" Size: 0x%" PRIx64 "\n", key_block->key_block_size); |
| printf(" Flags: %" PRIu64 " ", key_block->key_block_flags); |
| if (key_block->key_block_flags & KEY_BLOCK_FLAG_DEVELOPER_0) |
| printf(" !DEV"); |
| if (key_block->key_block_flags & KEY_BLOCK_FLAG_DEVELOPER_1) |
| printf(" DEV"); |
| if (key_block->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_0) |
| printf(" !REC"); |
| if (key_block->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_1) |
| printf(" REC"); |
| printf("\n"); |
| 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"); |
| |
| rsa = PublicKeyToRSA(&key_block->data_key); |
| if (!rsa) { |
| error("Error parsing data key.\n"); |
| goto verify_exit; |
| } |
| |
| /* Verify preamble */ |
| preamble = bp->preamble; |
| if (0 != VerifyKernelPreamble( |
| preamble, bp->blob_size - key_block->key_block_size, rsa)) { |
| error("Error verifying preamble.\n"); |
| goto verify_exit; |
| } |
| now += preamble->preamble_size; |
| |
| printf("Preamble:\n"); |
| printf(" Size: 0x%" PRIx64 "\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: 0x%" PRIx64 "\n", preamble->body_load_address); |
| printf(" Body size: 0x%" PRIx64 "\n", |
| preamble->body_signature.data_size); |
| printf(" Bootloader address: 0x%" PRIx64 "\n", |
| preamble->bootloader_address); |
| printf(" Bootloader size: 0x%" PRIx64 "\n", preamble->bootloader_size); |
| |
| /* Verify body */ |
| if (0 != VerifyData(bp->blob, bp->blob_size, &preamble->body_signature, |
| rsa)) { |
| error("Error verifying kernel body.\n"); |
| goto verify_exit; |
| } |
| printf("Body verification succeeded.\n"); |
| |
| rv = 0; |
| |
| if (!verbose) { |
| goto verify_exit; |
| } |
| |
| printf("Config:\n%s\n", BpCmdLineLocation(bp)); |
| |
| verify_exit: |
| FreeBlob(bp); |
| return rv; |
| } |
| |
| |
| int main(int argc, char* argv[]) { |
| char* filename = NULL; |
| char* oldfile = 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; |
| int vblockonly = 0; |
| int verbose = 0; |
| uint64_t pad = DEFAULT_PADDING; |
| int mode = 0; |
| int parse_error = 0; |
| char* e; |
| int i,r; |
| blob_t *bp; |
| |
| |
| char *progname = strrchr(argv[0], '/'); |
| if (progname) |
| progname++; |
| else |
| progname = argv[0]; |
| |
| while (((i = getopt_long(argc, argv, ":", long_opts, NULL)) != -1) && |
| !parse_error) { |
| switch (i) { |
| default: |
| case '?': |
| /* Unhandled option */ |
| parse_error = 1; |
| break; |
| |
| case 0: |
| /* silently handled option */ |
| break; |
| |
| case OPT_MODE_PACK: |
| case OPT_MODE_REPACK: |
| case OPT_MODE_VERIFY: |
| if (mode && (mode != i)) { |
| fprintf(stderr, "Only single mode can be specified\n"); |
| parse_error = 1; |
| break; |
| } |
| mode = i; |
| filename = optarg; |
| break; |
| |
| case OPT_OLDBLOB: |
| oldfile = 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_VBLOCKONLY: |
| vblockonly = 1; |
| break; |
| |
| case OPT_VERSION: |
| version = strtoul(optarg, &e, 0); |
| if (!*optarg || (e && *e)) { |
| fprintf(stderr, "Invalid --version\n"); |
| parse_error = 1; |
| } |
| break; |
| |
| case OPT_PAD: |
| pad = strtoul(optarg, &e, 0); |
| if (!*optarg || (e && *e)) { |
| fprintf(stderr, "Invalid --pad\n"); |
| parse_error = 1; |
| } |
| break; |
| |
| case OPT_VERBOSE: |
| verbose = 1; |
| break; |
| } |
| } |
| |
| if (parse_error) |
| return PrintHelp(progname); |
| |
| switch(mode) { |
| case OPT_MODE_PACK: |
| bp = NewBlob(version, vmlinuz, bootloader, config_file); |
| if (!bp) |
| return 1; |
| r = Pack(filename, key_block_file, signprivate, bp, pad, vblockonly); |
| FreeBlob(bp); |
| return r; |
| |
| case OPT_MODE_REPACK: |
| if (!config_file && !key_block_file && !version) { |
| fprintf(stderr, |
| "You must supply at least one of " |
| "--config, --keyblock or --version\n"); |
| return 1; |
| } |
| |
| bp = OldBlob(oldfile); |
| if (!bp) |
| return 1; |
| r = ReplaceConfig(bp, config_file); |
| if (!r) { |
| if (version) { |
| bp->kernel_version = version; |
| } |
| r = Pack(filename, key_block_file, signprivate, bp, pad, vblockonly); |
| } |
| FreeBlob(bp); |
| return r; |
| |
| case OPT_MODE_VERIFY: |
| return Verify(filename, signpubkey, verbose); |
| |
| default: |
| fprintf(stderr, |
| "You must specify a mode: --pack, --repack or --verify\n"); |
| return PrintHelp(progname); |
| } |
| } |