blob: 893f889cb39b9a8b15fc8fba22b43ed5deadfdf2 [file] [log] [blame]
Randall Spangler7d6898d2010-06-11 09:22:13 -07001/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 *
5 * Verified boot kernel utility
6 */
7
Bill Richardsona08b5c92010-06-30 21:59:43 -07008#include <errno.h>
Randall Spangler7d6898d2010-06-11 09:22:13 -07009#include <getopt.h>
10#include <inttypes.h> /* For PRIu64 */
Bill Richardson249677d2010-06-23 11:16:37 -070011#include <stdarg.h>
Randall Spangler7d6898d2010-06-11 09:22:13 -070012#include <stddef.h>
13#include <stdio.h>
14#include <stdlib.h>
Bill Richardsona08b5c92010-06-30 21:59:43 -070015#include <string.h>
16#include <sys/stat.h>
17#include <sys/types.h>
Randall Spangler7d6898d2010-06-11 09:22:13 -070018#include <unistd.h>
19
20#include "cryptolib.h"
21#include "host_common.h"
22#include "kernel_blob.h"
23#include "vboot_common.h"
24
25
Bill Richardson249677d2010-06-23 11:16:37 -070026/* Global opt */
27static int opt_debug = 0;
28
Bill Richardsona08b5c92010-06-30 21:59:43 -070029static const int DEFAULT_PADDING = 65536;
Bill Richardson249677d2010-06-23 11:16:37 -070030
Randall Spangler7d6898d2010-06-11 09:22:13 -070031/* Command line options */
32enum {
33 OPT_MODE_PACK = 1000,
Bill Richardsona08b5c92010-06-30 21:59:43 -070034 OPT_MODE_REPACK,
Randall Spangler7d6898d2010-06-11 09:22:13 -070035 OPT_MODE_VERIFY,
Bill Richardsona08b5c92010-06-30 21:59:43 -070036 OPT_OLDBLOB,
Randall Spangler7d6898d2010-06-11 09:22:13 -070037 OPT_KEYBLOCK,
38 OPT_SIGNPUBKEY,
39 OPT_SIGNPRIVATE,
40 OPT_VERSION,
41 OPT_VMLINUZ,
42 OPT_BOOTLOADER,
43 OPT_CONFIG,
Bill Richardsona08b5c92010-06-30 21:59:43 -070044 OPT_VBLOCKONLY,
Randall Spangler7d6898d2010-06-11 09:22:13 -070045 OPT_PAD,
vbendebb2b0fcc2010-07-15 15:09:47 -070046 OPT_VERBOSE,
Randall Spangler7d6898d2010-06-11 09:22:13 -070047};
48
49static struct option long_opts[] = {
50 {"pack", 1, 0, OPT_MODE_PACK },
Bill Richardsona08b5c92010-06-30 21:59:43 -070051 {"repack", 1, 0, OPT_MODE_REPACK },
Randall Spangler7d6898d2010-06-11 09:22:13 -070052 {"verify", 1, 0, OPT_MODE_VERIFY },
Bill Richardsona08b5c92010-06-30 21:59:43 -070053 {"oldblob", 1, 0, OPT_OLDBLOB },
Randall Spangler7d6898d2010-06-11 09:22:13 -070054 {"keyblock", 1, 0, OPT_KEYBLOCK },
55 {"signpubkey", 1, 0, OPT_SIGNPUBKEY },
56 {"signprivate", 1, 0, OPT_SIGNPRIVATE },
57 {"version", 1, 0, OPT_VERSION },
58 {"vmlinuz", 1, 0, OPT_VMLINUZ },
59 {"bootloader", 1, 0, OPT_BOOTLOADER },
60 {"config", 1, 0, OPT_CONFIG },
Bill Richardsona08b5c92010-06-30 21:59:43 -070061 {"vblockonly", 0, 0, OPT_VBLOCKONLY },
Randall Spangler7d6898d2010-06-11 09:22:13 -070062 {"pad", 1, 0, OPT_PAD },
vbendebb2b0fcc2010-07-15 15:09:47 -070063 {"verbose", 0, 0, OPT_VERBOSE },
Bill Richardson249677d2010-06-23 11:16:37 -070064 {"debug", 0, &opt_debug, 1 },
Randall Spangler7d6898d2010-06-11 09:22:13 -070065 {NULL, 0, 0, 0}
66};
67
68
69/* Print help and return error */
Bill Richardsona08b5c92010-06-30 21:59:43 -070070static int PrintHelp(char *progname) {
71 fprintf(stderr,
72 "This program creates, signs, and verifies the kernel blob\n");
73 fprintf(stderr,
74 "\n"
75 "Usage: %s --pack <file> [PARAMETERS]\n"
76 "\n"
77 " Required parameters:\n"
78 " --keyblock <file> Key block in .keyblock format\n"
79 " --signprivate <file> Signing private key in .pem format\n"
80 " --version <number> Kernel version\n"
81 " --vmlinuz <file> Linux kernel bzImage file\n"
82 " --bootloader <file> Bootloader stub\n"
vbendebb2b0fcc2010-07-15 15:09:47 -070083 " --config <file> Command line file\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -070084 "\n"
85 " Optional:\n"
86 " --pad <number> Verification padding size in bytes\n"
87 " --vblockonly Emit just the verification blob\n",
88 progname);
89 fprintf(stderr,
90 "\nOR\n\n"
91 "Usage: %s --repack <file> [PARAMETERS]\n"
92 "\n"
vbendebb2b0fcc2010-07-15 15:09:47 -070093 " Required parameters (of --keyblock and --config at least "
94 "one is required):\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -070095 " --keyblock <file> Key block in .keyblock format\n"
96 " --signprivate <file> Signing private key in .pem format\n"
97 " --oldblob <file> Previously packed kernel blob\n"
vbendebb2b0fcc2010-07-15 15:09:47 -070098 " --config <file> New command line file\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -070099 "\n"
100 " Optional:\n"
101 " --pad <number> Verification padding size in bytes\n"
102 " --vblockonly Emit just the verification blob\n",
103 progname);
104 fprintf(stderr,
105 "\nOR\n\n"
106 "Usage: %s --verify <file> [PARAMETERS]\n"
107 "\n"
108 " Required parameters:\n"
109 " --signpubkey <file> Signing public key in .vbpubk format\n"
vbendebb2b0fcc2010-07-15 15:09:47 -0700110 "\n"
111 " Optional:\n"
112 " --verbose Print a more detailed report\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -0700113 "\n",
114 progname);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700115 return 1;
116}
117
Bill Richardson249677d2010-06-23 11:16:37 -0700118static void Debug(const char *format, ...) {
119 if (!opt_debug)
120 return;
121
122 va_list ap;
123 va_start(ap, format);
124 fprintf(stderr, "DEBUG: ");
125 vfprintf(stderr, format, ap);
126 va_end(ap);
127}
128
Randall Spangler7d6898d2010-06-11 09:22:13 -0700129
130/* Return the smallest integral multiple of [alignment] that is equal
131 * to or greater than [val]. Used to determine the number of
132 * pages/sectors/blocks/whatever needed to contain [val]
133 * items/bytes/etc. */
134static uint64_t roundup(uint64_t val, uint64_t alignment) {
135 uint64_t rem = val % alignment;
136 if ( rem )
137 return val + (alignment - rem);
138 return val;
139}
140
141
142/* Match regexp /\b--\b/ to delimit the start of the kernel commandline. If we
143 * don't find one, we'll use the whole thing. */
144static unsigned int find_cmdline_start(char *input, unsigned int max_len) {
145 int start = 0;
146 int i;
147 for(i = 0; i < max_len - 1 && input[i]; i++) {
148 if ('-' == input[i] && '-' == input[i + 1]) { /* found a "--" */
149 if ((i == 0 || ' ' == input[i - 1]) && /* nothing before it */
150 (i + 2 >= max_len || ' ' == input[i+2])) { /* nothing after it */
151 start = i+2; /* note: hope there's a trailing '\0' */
152 break;
153 }
154 }
155 }
156 while(' ' == input[start]) /* skip leading spaces */
157 start++;
158
159 return start;
160}
161
162
Bill Richardsona08b5c92010-06-30 21:59:43 -0700163typedef struct blob_s {
164 /* Stuff needed by VbKernelPreambleHeader */
165 uint64_t kernel_version;
166 uint64_t bootloader_address;
167 uint64_t bootloader_size;
168 /* Raw kernel blob data */
169 uint64_t blob_size;
170 uint8_t *blob;
vbendebb2b0fcc2010-07-15 15:09:47 -0700171
172 /* these fields are not always initialized */
173 VbKernelPreambleHeader* preamble;
174 VbKeyBlockHeader* key_block;
175 uint8_t *buf;
176
Bill Richardsona08b5c92010-06-30 21:59:43 -0700177} blob_t;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700178
vbendebb2b0fcc2010-07-15 15:09:47 -0700179/* Given a blob return the location of the kernel command line buffer. */
180static char* BpCmdLineLocation(blob_t *bp)
181{
182 return (char*)(bp->blob + bp->bootloader_address - CROS_32BIT_ENTRY_ADDR -
183 CROS_CONFIG_SIZE - CROS_PARAMS_SIZE);
184}
Bill Richardsona08b5c92010-06-30 21:59:43 -0700185
186static void FreeBlob(blob_t *bp) {
187 if (bp) {
188 if (bp->blob)
189 Free(bp->blob);
vbendebb2b0fcc2010-07-15 15:09:47 -0700190 if (bp->buf)
191 Free(bp->buf);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700192 Free(bp);
193 }
194}
195
vbendebb2b0fcc2010-07-15 15:09:47 -0700196/*
197 * Read the kernel command line from a file. Get rid of \n characters along
198 * the way and verify that the line fits into a 4K buffer.
199 *
200 * Return the buffer contaning the line on success (and set the line length
201 * using the passed in parameter), or NULL in case something goes wrong.
202 */
203static uint8_t* ReadConfigFile(const char* config_file, uint64_t* config_size)
204{
205 uint8_t* config_buf;
206 int ii;
207
208 config_buf = ReadFile(config_file, config_size);
209 Debug(" config file size=0x%" PRIx64 "\n", *config_size);
210 if (CROS_CONFIG_SIZE <= *config_size) { /* need room for trailing '\0' */
211 error("Config file %s is too large (>= %d bytes)\n",
212 config_file, CROS_CONFIG_SIZE);
213 return NULL;
214 }
215
216 /* Replace newlines with spaces */
217 for (ii = 0; ii < *config_size; ii++) {
218 if ('\n' == config_buf[ii]) {
219 config_buf[ii] = ' ';
220 }
221 }
222 return config_buf;
223}
224
Bill Richardsona08b5c92010-06-30 21:59:43 -0700225/* Create a blob from its components */
226static blob_t *NewBlob(uint64_t version,
227 const char* vmlinuz,
228 const char* bootloader_file,
229 const char* config_file) {
230 blob_t *bp;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700231 struct linux_kernel_header *lh = 0;
232 struct linux_kernel_params *params = 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700233 uint8_t* config_buf;
234 uint64_t config_size;
235 uint8_t* bootloader_buf;
236 uint64_t bootloader_size;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700237 uint8_t* kernel_buf;
238 uint64_t kernel_size;
239 uint64_t kernel32_start = 0;
240 uint64_t kernel32_size = 0;
241 uint32_t cmdline_addr;
242 uint8_t* blob = NULL;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700243 uint64_t now = 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700244
Randall Spangler7d6898d2010-06-11 09:22:13 -0700245 if (!vmlinuz || !bootloader_file || !config_file) {
246 error("Must specify all input files\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700247 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700248 }
249
Bill Richardsona08b5c92010-06-30 21:59:43 -0700250 bp = (blob_t *)Malloc(sizeof(blob_t));
251 if (!bp) {
252 error("Couldn't allocate bytes for blob_t.\n");
253 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700254 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700255
256 Memset(bp, 0, sizeof(*bp));
Bill Richardsona08b5c92010-06-30 21:59:43 -0700257 bp->kernel_version = version;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700258
259 /* Read the config file */
Bill Richardson249677d2010-06-23 11:16:37 -0700260 Debug("Reading %s\n", config_file);
vbendebb2b0fcc2010-07-15 15:09:47 -0700261 config_buf = ReadConfigFile(config_file, &config_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700262 if (!config_buf)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700263 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700264
265 /* Read the bootloader */
Bill Richardson249677d2010-06-23 11:16:37 -0700266 Debug("Reading %s\n", bootloader_file);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700267 bootloader_buf = ReadFile(bootloader_file, &bootloader_size);
268 if (!bootloader_buf)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700269 return 0;
Bill Richardson249677d2010-06-23 11:16:37 -0700270 Debug(" bootloader file size=0x%" PRIx64 "\n", bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700271
272 /* Read the kernel */
Bill Richardson249677d2010-06-23 11:16:37 -0700273 Debug("Reading %s\n", vmlinuz);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700274 kernel_buf = ReadFile(vmlinuz, &kernel_size);
275 if (!kernel_buf)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700276 return 0;
Bill Richardson249677d2010-06-23 11:16:37 -0700277 Debug(" kernel file size=0x%" PRIx64 "\n", kernel_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700278 if (!kernel_size) {
279 error("Empty kernel file\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700280 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700281 }
282
283 /* The first part of vmlinuz is a header, followed by a real-mode
284 * boot stub. We only want the 32-bit part. */
285 lh = (struct linux_kernel_header *)kernel_buf;
286 kernel32_start = (lh->setup_sects + 1) << 9;
287 if (kernel32_start >= kernel_size) {
288 error("Malformed kernel\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700289 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700290 }
291 kernel32_size = kernel_size - kernel32_start;
Bill Richardson249677d2010-06-23 11:16:37 -0700292 Debug(" kernel32_start=0x%" PRIx64 "\n", kernel32_start);
293 Debug(" kernel32_size=0x%" PRIx64 "\n", kernel32_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700294
295 /* Allocate and zero the blob we need. */
Bill Richardsona08b5c92010-06-30 21:59:43 -0700296 bp->blob_size = roundup(kernel32_size, CROS_ALIGN) +
Randall Spangler7d6898d2010-06-11 09:22:13 -0700297 CROS_CONFIG_SIZE +
298 CROS_PARAMS_SIZE +
299 roundup(bootloader_size, CROS_ALIGN);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700300 blob = (uint8_t *)Malloc(bp->blob_size);
301 Debug("blob_size=0x%" PRIx64 "\n", bp->blob_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700302 if (!blob) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700303 error("Couldn't allocate %ld bytes.\n", bp->blob_size);
304 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700305 }
Bill Richardsona08b5c92010-06-30 21:59:43 -0700306 Memset(blob, 0, bp->blob_size);
307 bp->blob = blob;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700308
309 /* Copy the 32-bit kernel. */
Bill Richardson249677d2010-06-23 11:16:37 -0700310 Debug("kernel goes at blob+=0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700311 if (kernel32_size)
312 Memcpy(blob + now, kernel_buf + kernel32_start, kernel32_size);
313 now += roundup(now + kernel32_size, CROS_ALIGN);
314
Bill Richardson249677d2010-06-23 11:16:37 -0700315 Debug("config goes at blob+0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700316 /* Find the load address of the commandline. We'll need it later. */
317 cmdline_addr = CROS_32BIT_ENTRY_ADDR + now +
318 find_cmdline_start((char *)config_buf, config_size);
Bill Richardson249677d2010-06-23 11:16:37 -0700319 Debug(" cmdline_addr=0x%" PRIx64 "\n", cmdline_addr);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700320
321 /* Copy the config. */
322 if (config_size)
323 Memcpy(blob + now, config_buf, config_size);
324 now += CROS_CONFIG_SIZE;
325
326 /* The zeropage data is next. Overlay the linux_kernel_header onto it, and
327 * tweak a few fields. */
Bill Richardson249677d2010-06-23 11:16:37 -0700328 Debug("params goes at blob+=0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700329 params = (struct linux_kernel_params *)(blob + now);
330 Memcpy(&(params->setup_sects), &(lh->setup_sects),
331 sizeof(*lh) - offsetof(struct linux_kernel_header, setup_sects));
332 params->boot_flag = 0;
333 params->ramdisk_image = 0; /* we don't support initrd */
334 params->ramdisk_size = 0;
335 params->type_of_loader = 0xff;
336 params->cmd_line_ptr = cmdline_addr;
337 now += CROS_PARAMS_SIZE;
338
339 /* Finally, append the bootloader. Remember where it will load in
340 * memory, too. */
Bill Richardson249677d2010-06-23 11:16:37 -0700341 Debug("bootloader goes at blob+=0x%" PRIx64 "\n", now);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700342 bp->bootloader_address = CROS_32BIT_ENTRY_ADDR + now;
343 bp->bootloader_size = roundup(bootloader_size, CROS_ALIGN);
344 Debug(" bootloader_address=0x%" PRIx64 "\n", bp->bootloader_address);
345 Debug(" bootloader_size=0x%" PRIx64 "\n", bp->bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700346 if (bootloader_size)
347 Memcpy(blob + now, bootloader_buf, bootloader_size);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700348 now += bp->bootloader_size;
Bill Richardson249677d2010-06-23 11:16:37 -0700349 Debug("end of blob is 0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700350
351 /* Free input buffers */
352 Free(kernel_buf);
353 Free(config_buf);
354 Free(bootloader_buf);
355
Bill Richardsona08b5c92010-06-30 21:59:43 -0700356 /* Success */
357 return bp;
358}
359
360
361/* Pull the blob_t stuff out of a prepacked kernel blob file */
362static blob_t *OldBlob(const char* filename) {
vbendebb2b0fcc2010-07-15 15:09:47 -0700363 FILE* fp = NULL;
364 blob_t *bp = NULL;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700365 struct stat statbuf;
366 VbKeyBlockHeader* key_block;
367 VbKernelPreambleHeader* preamble;
368 uint64_t now = 0;
vbendebb2b0fcc2010-07-15 15:09:47 -0700369 uint8_t* buf = NULL;
370 int ret_error = 1;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700371
372 if (!filename) {
373 error("Must specify prepacked blob to read\n");
374 return 0;
375 }
376
377 if (0 != stat(filename, &statbuf)) {
378 error("unable to stat %s: %s\n", filename, strerror(errno));
379 return 0;
380 }
381
382 Debug("%s size is 0x%" PRIx64 "\n", filename, statbuf.st_size);
383 if (statbuf.st_size < DEFAULT_PADDING) {
384 error("%s is too small to be a valid kernel blob\n");
385 return 0;
386 }
387
388 Debug("Reading %s\n", filename);
389 fp = fopen(filename, "rb");
390 if (!fp) {
391 error("Unable to open file %s: %s\n", filename, strerror(errno));
392 return 0;
393 }
394
vbendebb2b0fcc2010-07-15 15:09:47 -0700395 buf = Malloc(DEFAULT_PADDING);
396 if (!buf) {
397 error("Unable to allocate padding\n");
398 goto unwind_oldblob;
399 }
400
401 if (1 != fread(buf, DEFAULT_PADDING, 1, fp)) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700402 error("Unable to read header from %s: %s\n", filename, strerror(errno));
vbendebb2b0fcc2010-07-15 15:09:47 -0700403 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700404 }
405
406 /* Skip the key block */
407 key_block = (VbKeyBlockHeader*)buf;
408 Debug("Keyblock is 0x%" PRIx64 " bytes\n", key_block->key_block_size);
409 now += key_block->key_block_size;
410 if (now > statbuf.st_size) {
411 error("key_block_size advances past the end of the blob\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700412 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700413 }
414
415 /* Skip the preamble */
416 preamble = (VbKernelPreambleHeader*)(buf + now);
417 Debug("Preamble is 0x%" PRIx64 " bytes\n", preamble->preamble_size);
418 now += preamble->preamble_size;
419 if (now > statbuf.st_size) {
420 error("preamble_size advances past the end of the blob\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700421 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700422 }
423
424 /* Go find the kernel blob */
425 Debug("kernel blob is at offset 0x%" PRIx64 "\n", now);
426 if (0 != fseek(fp, now, SEEK_SET)) {
427 error("Unable to seek to 0x%" PRIx64 " in %s: %s\n", now, filename,
428 strerror(errno));
vbendebb2b0fcc2010-07-15 15:09:47 -0700429 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700430 }
431
432 /* Remember what we've got */
433 bp = (blob_t *)Malloc(sizeof(blob_t));
434 if (!bp) {
435 error("Couldn't allocate bytes for blob_t.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700436 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700437 }
438
vbendebb2b0fcc2010-07-15 15:09:47 -0700439 bp->buf = buf;
440 bp->key_block = key_block;
441 bp->preamble = preamble;
442
Bill Richardsona08b5c92010-06-30 21:59:43 -0700443 bp->kernel_version = preamble->kernel_version;
444 bp->bootloader_address = preamble->bootloader_address;
445 bp->bootloader_size = preamble->bootloader_size;
446 bp->blob_size = preamble->body_signature.data_size;
447
448 Debug(" kernel_version = %d\n", bp->kernel_version);
449 Debug(" bootloader_address = 0x%" PRIx64 "\n", bp->bootloader_address);
450 Debug(" bootloader_size = 0x%" PRIx64 "\n", bp->bootloader_size);
451 Debug(" blob_size = 0x%" PRIx64 "\n", bp->blob_size);
452
453 bp->blob = (uint8_t *)Malloc(bp->blob_size);
454 if (!bp->blob) {
455 error("Couldn't allocate 0x%" PRIx64 " bytes for blob_t.\n", bp->blob_size);
vbendebb2b0fcc2010-07-15 15:09:47 -0700456 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700457 }
458
459 /* read it in */
460 if (1 != fread(bp->blob, bp->blob_size, 1, fp)) {
461 error("Unable to read kernel blob from %s: %s\n", filename, strerror(errno));
vbendebb2b0fcc2010-07-15 15:09:47 -0700462 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700463 }
464
vbendebb2b0fcc2010-07-15 15:09:47 -0700465 ret_error = 0;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700466
vbendebb2b0fcc2010-07-15 15:09:47 -0700467 /* done */
468unwind_oldblob:
469 fclose(fp);
470 if (ret_error) {
471 if (bp) {
472 FreeBlob(bp);
473 bp = NULL;
474 } else if (buf) {
475 Free(buf);
476 }
477 }
Bill Richardsona08b5c92010-06-30 21:59:43 -0700478 return bp;
479}
480
481
482/* Pack a .kernel */
483static int Pack(const char* outfile, const char* keyblock_file,
484 const char* signprivate, blob_t *bp, uint64_t pad,
485 int vblockonly) {
486 VbPrivateKey* signing_key;
487 VbSignature* body_sig;
488 VbKernelPreambleHeader* preamble;
489 VbKeyBlockHeader* key_block;
490 uint64_t key_block_size;
491 FILE* f;
492 uint64_t i;
493
494 if (!outfile) {
495 error("Must specify output filename\n");
496 return 1;
497 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700498 if ((!keyblock_file && !bp->key_block) || !signprivate) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700499 error("Must specify all keys\n");
500 return 1;
501 }
502 if (!bp) {
503 error("Refusing to pack invalid kernel blob\n");
504 return 1;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700505 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700506
507 /* Get the key block and read the private key. */
508 if (keyblock_file) {
509 key_block = (VbKeyBlockHeader*)ReadFile(keyblock_file, &key_block_size);
510 if (!key_block) {
511 error("Error reading key block.\n");
512 return 1;
513 }
514 } else {
515 key_block = bp->key_block;
516 key_block_size = key_block->key_block_size;
517 }
518
Bill Richardsona08b5c92010-06-30 21:59:43 -0700519 if (pad < key_block->key_block_size) {
520 error("Pad too small\n");
521 return 1;
522 }
523
Bill Richardsonabf05502010-07-01 10:22:06 -0700524 signing_key = PrivateKeyRead(signprivate);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700525 if (!signing_key) {
526 error("Error reading signing key.\n");
527 return 1;
528 }
529
Randall Spangler7d6898d2010-06-11 09:22:13 -0700530 /* Sign the kernel data */
Bill Richardsona08b5c92010-06-30 21:59:43 -0700531 body_sig = CalculateSignature(bp->blob, bp->blob_size, signing_key);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700532 if (!body_sig) {
533 error("Error calculating body signature\n");
534 return 1;
535 }
536
537 /* Create preamble */
Bill Richardsona08b5c92010-06-30 21:59:43 -0700538 preamble = CreateKernelPreamble(bp->kernel_version,
Randall Spangler7d6898d2010-06-11 09:22:13 -0700539 CROS_32BIT_ENTRY_ADDR,
Bill Richardsona08b5c92010-06-30 21:59:43 -0700540 bp->bootloader_address,
541 bp->bootloader_size,
Randall Spangler7d6898d2010-06-11 09:22:13 -0700542 body_sig,
543 pad - key_block_size,
544 signing_key);
545 if (!preamble) {
546 error("Error creating preamble.\n");
547 return 1;
548 }
549
550 /* Write the output file */
Bill Richardson249677d2010-06-23 11:16:37 -0700551 Debug("writing %s...\n", outfile);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700552 f = fopen(outfile, "wb");
553 if (!f) {
554 error("Can't open output file %s\n", outfile);
555 return 1;
556 }
Bill Richardson249677d2010-06-23 11:16:37 -0700557 Debug("0x%" PRIx64 " bytes of key_block\n", key_block_size);
558 Debug("0x%" PRIx64 " bytes of preamble\n", preamble->preamble_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700559 i = ((1 != fwrite(key_block, key_block_size, 1, f)) ||
Bill Richardsona08b5c92010-06-30 21:59:43 -0700560 (1 != fwrite(preamble, preamble->preamble_size, 1, f)));
Randall Spangler7d6898d2010-06-11 09:22:13 -0700561 if (i) {
562 error("Can't write output file %s\n", outfile);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700563 fclose(f);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700564 unlink(outfile);
565 return 1;
566 }
567
Bill Richardsona08b5c92010-06-30 21:59:43 -0700568 if (!vblockonly) {
569 Debug("0x%" PRIx64 " bytes of blob\n", bp->blob_size);
570 i = (1 != fwrite(bp->blob, bp->blob_size, 1, f));
571 if (i) {
572 error("Can't write output file %s\n", outfile);
573 fclose(f);
574 unlink(outfile);
575 return 1;
576 }
577 }
578
579 fclose(f);
580
Randall Spangler7d6898d2010-06-11 09:22:13 -0700581 /* Success */
582 return 0;
583}
584
vbendebb2b0fcc2010-07-15 15:09:47 -0700585/*
586 * Replace kernel command line in a blob representing a kernel.
587 */
588static int ReplaceConfig(blob_t* bp, const char* config_file)
589{
590 uint8_t* new_conf;
591 uint64_t config_size;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700592
vbendebb2b0fcc2010-07-15 15:09:47 -0700593 if (!config_file) {
594 return 0;
595 }
596
597 new_conf = ReadConfigFile(config_file, &config_size);
598 if (!new_conf) {
599 return 1;
600 }
601
602 /* fill the config buffer with zeros */
603 Memset(BpCmdLineLocation(bp), 0, CROS_CONFIG_SIZE);
604 Memcpy(BpCmdLineLocation(bp), new_conf, config_size);
605 Free(new_conf);
606 return 0;
607}
608
609static int Verify(const char* infile, const char* signpubkey, int verbose) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700610
611 VbKeyBlockHeader* key_block;
612 VbKernelPreambleHeader* preamble;
613 VbPublicKey* data_key;
614 VbPublicKey* sign_key;
615 RSAPublicKey* rsa;
vbendebb2b0fcc2010-07-15 15:09:47 -0700616 blob_t* bp;
617 uint64_t now;
618 int rv = 1;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700619
620 if (!infile || !signpubkey) {
621 error("Must specify filename and signpubkey\n");
622 return 1;
623 }
624
625 /* Read public signing key */
626 sign_key = PublicKeyRead(signpubkey);
627 if (!sign_key) {
628 error("Error reading signpubkey.\n");
629 return 1;
630 }
631
632 /* Read blob */
vbendebb2b0fcc2010-07-15 15:09:47 -0700633 bp = OldBlob(infile);
634 if (!bp) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700635 error("Error reading input file\n");
636 return 1;
637 }
638
639 /* Verify key block */
vbendebb2b0fcc2010-07-15 15:09:47 -0700640 key_block = bp->key_block;
641 if (0 != KeyBlockVerify(key_block, bp->blob_size, sign_key)) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700642 error("Error verifying key block.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700643 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700644 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700645 now = key_block->key_block_size;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700646
647 printf("Key block:\n");
648 data_key = &key_block->data_key;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700649 printf(" Size: 0x%" PRIx64 "\n", key_block->key_block_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700650 printf(" Data key algorithm: %" PRIu64 " %s\n", data_key->algorithm,
651 (data_key->algorithm < kNumAlgorithms ?
652 algo_strings[data_key->algorithm] : "(invalid)"));
653 printf(" Data key version: %" PRIu64 "\n", data_key->key_version);
654 printf(" Flags: %" PRIu64 "\n", key_block->key_block_flags);
655
656 rsa = PublicKeyToRSA(&key_block->data_key);
657 if (!rsa) {
658 error("Error parsing data key.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700659 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700660 }
661
662 /* Verify preamble */
vbendebb2b0fcc2010-07-15 15:09:47 -0700663 preamble = bp->preamble;
Randall Spangler87c13d82010-07-19 10:35:40 -0700664 if (0 != VerifyKernelPreamble(
vbendebb2b0fcc2010-07-15 15:09:47 -0700665 preamble, bp->blob_size - key_block->key_block_size, rsa)) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700666 error("Error verifying preamble.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700667 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700668 }
669 now += preamble->preamble_size;
670
671 printf("Preamble:\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700672 printf(" Size: 0x%" PRIx64 "\n", preamble->preamble_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700673 printf(" Header version: %" PRIu32 ".%" PRIu32"\n",
674 preamble->header_version_major, preamble->header_version_minor);
675 printf(" Kernel version: %" PRIu64 "\n", preamble->kernel_version);
Bill Richardson249677d2010-06-23 11:16:37 -0700676 printf(" Body load address: 0x%" PRIx64 "\n", preamble->body_load_address);
677 printf(" Body size: 0x%" PRIx64 "\n",
Randall Spangler7d6898d2010-06-11 09:22:13 -0700678 preamble->body_signature.data_size);
Randall Spangler87c13d82010-07-19 10:35:40 -0700679 printf(" Bootloader address: 0x%" PRIx64 "\n",
680 preamble->bootloader_address);
Bill Richardson249677d2010-06-23 11:16:37 -0700681 printf(" Bootloader size: 0x%" PRIx64 "\n", preamble->bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700682
683 /* Verify body */
Randall Spangler87c13d82010-07-19 10:35:40 -0700684 if (0 != VerifyData(bp->blob, bp->blob_size, &preamble->body_signature,
685 rsa)) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700686 error("Error verifying kernel body.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700687 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700688 }
689 printf("Body verification succeeded.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700690
691 rv = 0;
692
693 if (!verbose) {
694 goto verify_exit;
695 }
696
697 printf("Config:\n%s\n", BpCmdLineLocation(bp));
698
699verify_exit:
700 FreeBlob(bp);
701 return rv;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700702}
703
704
705int main(int argc, char* argv[]) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700706 char* filename = NULL;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700707 char* oldfile = NULL;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700708 char* key_block_file = NULL;
709 char* signpubkey = NULL;
710 char* signprivate = NULL;
711 uint64_t version = 0;
712 char* vmlinuz = NULL;
713 char* bootloader = NULL;
714 char* config_file = NULL;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700715 int vblockonly = 0;
vbendebb2b0fcc2010-07-15 15:09:47 -0700716 int verbose = 0;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700717 uint64_t pad = DEFAULT_PADDING;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700718 int mode = 0;
719 int parse_error = 0;
720 char* e;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700721 int i,r;
722 blob_t *bp;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700723
Bill Richardsona08b5c92010-06-30 21:59:43 -0700724
725 char *progname = strrchr(argv[0], '/');
726 if (progname)
727 progname++;
728 else
729 progname = argv[0];
730
vbendebb2b0fcc2010-07-15 15:09:47 -0700731 while (((i = getopt_long(argc, argv, ":", long_opts, NULL)) != -1) &&
732 !parse_error) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700733 switch (i) {
vbendebb2b0fcc2010-07-15 15:09:47 -0700734 default:
Randall Spangler7d6898d2010-06-11 09:22:13 -0700735 case '?':
736 /* Unhandled option */
Randall Spangler7d6898d2010-06-11 09:22:13 -0700737 parse_error = 1;
738 break;
739
740 case OPT_MODE_PACK:
Bill Richardsona08b5c92010-06-30 21:59:43 -0700741 case OPT_MODE_REPACK:
Randall Spangler7d6898d2010-06-11 09:22:13 -0700742 case OPT_MODE_VERIFY:
vbendebb2b0fcc2010-07-15 15:09:47 -0700743 if (mode && (mode != i)) {
744 fprintf(stderr, "Only single mode can be specified\n");
745 parse_error = 1;
746 break;
747 }
Randall Spangler7d6898d2010-06-11 09:22:13 -0700748 mode = i;
749 filename = optarg;
750 break;
751
Bill Richardsona08b5c92010-06-30 21:59:43 -0700752 case OPT_OLDBLOB:
753 oldfile = optarg;
754 break;
755
Randall Spangler7d6898d2010-06-11 09:22:13 -0700756 case OPT_KEYBLOCK:
757 key_block_file = optarg;
758 break;
759
760 case OPT_SIGNPUBKEY:
761 signpubkey = optarg;
762 break;
763
764 case OPT_SIGNPRIVATE:
765 signprivate = optarg;
766 break;
767
768 case OPT_VMLINUZ:
769 vmlinuz = optarg;
770 break;
771
772 case OPT_BOOTLOADER:
773 bootloader = optarg;
774 break;
775
776 case OPT_CONFIG:
777 config_file = optarg;
778 break;
779
Bill Richardsona08b5c92010-06-30 21:59:43 -0700780 case OPT_VBLOCKONLY:
781 vblockonly = 1;
782 break;
783
Randall Spangler7d6898d2010-06-11 09:22:13 -0700784 case OPT_VERSION:
785 version = strtoul(optarg, &e, 0);
786 if (!*optarg || (e && *e)) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700787 fprintf(stderr, "Invalid --version\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700788 parse_error = 1;
789 }
790 break;
791
792 case OPT_PAD:
793 pad = strtoul(optarg, &e, 0);
794 if (!*optarg || (e && *e)) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700795 fprintf(stderr, "Invalid --pad\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700796 parse_error = 1;
797 }
798 break;
vbendebb2b0fcc2010-07-15 15:09:47 -0700799
800 case OPT_VERBOSE:
801 verbose = 1;
802 break;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700803 }
804 }
805
806 if (parse_error)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700807 return PrintHelp(progname);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700808
809 switch(mode) {
810 case OPT_MODE_PACK:
Bill Richardsona08b5c92010-06-30 21:59:43 -0700811 bp = NewBlob(version, vmlinuz, bootloader, config_file);
812 if (!bp)
813 return 1;
814 r = Pack(filename, key_block_file, signprivate, bp, pad, vblockonly);
815 FreeBlob(bp);
816 return r;
817
818 case OPT_MODE_REPACK:
vbendebb2b0fcc2010-07-15 15:09:47 -0700819 if (!config_file && !key_block_file) {
820 fprintf(stderr,
821 "You must supply at least one of --config and --keyblock\n");
822 return 1;
823 }
824
Bill Richardsona08b5c92010-06-30 21:59:43 -0700825 bp = OldBlob(oldfile);
826 if (!bp)
827 return 1;
vbendebb2b0fcc2010-07-15 15:09:47 -0700828 r = ReplaceConfig(bp, config_file);
829 if (!r) {
830 r = Pack(filename, key_block_file, signprivate, bp, pad, vblockonly);
831 }
Bill Richardsona08b5c92010-06-30 21:59:43 -0700832 FreeBlob(bp);
833 return r;
834
Randall Spangler7d6898d2010-06-11 09:22:13 -0700835 case OPT_MODE_VERIFY:
vbendebb2b0fcc2010-07-15 15:09:47 -0700836 return Verify(filename, signpubkey, verbose);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700837
Randall Spangler7d6898d2010-06-11 09:22:13 -0700838 default:
Bill Richardsona08b5c92010-06-30 21:59:43 -0700839 fprintf(stderr,
840 "You must specify a mode: --pack, --repack or --verify\n");
841 return PrintHelp(progname);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700842 }
843}