blob: 349cc8e012bba6fd3909768eb2775e0414741660 [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,
46};
47
48static struct option long_opts[] = {
49 {"pack", 1, 0, OPT_MODE_PACK },
Bill Richardsona08b5c92010-06-30 21:59:43 -070050 {"repack", 1, 0, OPT_MODE_REPACK },
Randall Spangler7d6898d2010-06-11 09:22:13 -070051 {"verify", 1, 0, OPT_MODE_VERIFY },
Bill Richardsona08b5c92010-06-30 21:59:43 -070052 {"oldblob", 1, 0, OPT_OLDBLOB },
Randall Spangler7d6898d2010-06-11 09:22:13 -070053 {"keyblock", 1, 0, OPT_KEYBLOCK },
54 {"signpubkey", 1, 0, OPT_SIGNPUBKEY },
55 {"signprivate", 1, 0, OPT_SIGNPRIVATE },
56 {"version", 1, 0, OPT_VERSION },
57 {"vmlinuz", 1, 0, OPT_VMLINUZ },
58 {"bootloader", 1, 0, OPT_BOOTLOADER },
59 {"config", 1, 0, OPT_CONFIG },
Bill Richardsona08b5c92010-06-30 21:59:43 -070060 {"vblockonly", 0, 0, OPT_VBLOCKONLY },
Randall Spangler7d6898d2010-06-11 09:22:13 -070061 {"pad", 1, 0, OPT_PAD },
Bill Richardson249677d2010-06-23 11:16:37 -070062 {"debug", 0, &opt_debug, 1 },
Randall Spangler7d6898d2010-06-11 09:22:13 -070063 {NULL, 0, 0, 0}
64};
65
66
67/* Print help and return error */
Bill Richardsona08b5c92010-06-30 21:59:43 -070068static int PrintHelp(char *progname) {
69 fprintf(stderr,
70 "This program creates, signs, and verifies the kernel blob\n");
71 fprintf(stderr,
72 "\n"
73 "Usage: %s --pack <file> [PARAMETERS]\n"
74 "\n"
75 " Required parameters:\n"
76 " --keyblock <file> Key block in .keyblock format\n"
77 " --signprivate <file> Signing private key in .pem format\n"
78 " --version <number> Kernel version\n"
79 " --vmlinuz <file> Linux kernel bzImage file\n"
80 " --bootloader <file> Bootloader stub\n"
81 " --config <file> Config file\n"
82 "\n"
83 " Optional:\n"
84 " --pad <number> Verification padding size in bytes\n"
85 " --vblockonly Emit just the verification blob\n",
86 progname);
87 fprintf(stderr,
88 "\nOR\n\n"
89 "Usage: %s --repack <file> [PARAMETERS]\n"
90 "\n"
91 " Required parameters:\n"
92 " --keyblock <file> Key block in .keyblock format\n"
93 " --signprivate <file> Signing private key in .pem format\n"
94 " --oldblob <file> Previously packed kernel blob\n"
95 "\n"
96 " Optional:\n"
97 " --pad <number> Verification padding size in bytes\n"
98 " --vblockonly Emit just the verification blob\n",
99 progname);
100 fprintf(stderr,
101 "\nOR\n\n"
102 "Usage: %s --verify <file> [PARAMETERS]\n"
103 "\n"
104 " Required parameters:\n"
105 " --signpubkey <file> Signing public key in .vbpubk format\n"
106 "\n",
107 progname);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700108 return 1;
109}
110
Bill Richardson249677d2010-06-23 11:16:37 -0700111static void Debug(const char *format, ...) {
112 if (!opt_debug)
113 return;
114
115 va_list ap;
116 va_start(ap, format);
117 fprintf(stderr, "DEBUG: ");
118 vfprintf(stderr, format, ap);
119 va_end(ap);
120}
121
Randall Spangler7d6898d2010-06-11 09:22:13 -0700122
123/* Return the smallest integral multiple of [alignment] that is equal
124 * to or greater than [val]. Used to determine the number of
125 * pages/sectors/blocks/whatever needed to contain [val]
126 * items/bytes/etc. */
127static uint64_t roundup(uint64_t val, uint64_t alignment) {
128 uint64_t rem = val % alignment;
129 if ( rem )
130 return val + (alignment - rem);
131 return val;
132}
133
134
135/* Match regexp /\b--\b/ to delimit the start of the kernel commandline. If we
136 * don't find one, we'll use the whole thing. */
137static unsigned int find_cmdline_start(char *input, unsigned int max_len) {
138 int start = 0;
139 int i;
140 for(i = 0; i < max_len - 1 && input[i]; i++) {
141 if ('-' == input[i] && '-' == input[i + 1]) { /* found a "--" */
142 if ((i == 0 || ' ' == input[i - 1]) && /* nothing before it */
143 (i + 2 >= max_len || ' ' == input[i+2])) { /* nothing after it */
144 start = i+2; /* note: hope there's a trailing '\0' */
145 break;
146 }
147 }
148 }
149 while(' ' == input[start]) /* skip leading spaces */
150 start++;
151
152 return start;
153}
154
155
Bill Richardsona08b5c92010-06-30 21:59:43 -0700156typedef struct blob_s {
157 /* Stuff needed by VbKernelPreambleHeader */
158 uint64_t kernel_version;
159 uint64_t bootloader_address;
160 uint64_t bootloader_size;
161 /* Raw kernel blob data */
162 uint64_t blob_size;
163 uint8_t *blob;
164} blob_t;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700165
Bill Richardsona08b5c92010-06-30 21:59:43 -0700166
167static void FreeBlob(blob_t *bp) {
168 if (bp) {
169 if (bp->blob)
170 Free(bp->blob);
171 Free(bp);
172 }
173}
174
175/* Create a blob from its components */
176static blob_t *NewBlob(uint64_t version,
177 const char* vmlinuz,
178 const char* bootloader_file,
179 const char* config_file) {
180 blob_t *bp;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700181 struct linux_kernel_header *lh = 0;
182 struct linux_kernel_params *params = 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700183 uint8_t* config_buf;
184 uint64_t config_size;
185 uint8_t* bootloader_buf;
186 uint64_t bootloader_size;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700187 uint8_t* kernel_buf;
188 uint64_t kernel_size;
189 uint64_t kernel32_start = 0;
190 uint64_t kernel32_size = 0;
191 uint32_t cmdline_addr;
192 uint8_t* blob = NULL;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700193 uint64_t now = 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700194 uint64_t i;
195
Randall Spangler7d6898d2010-06-11 09:22:13 -0700196 if (!vmlinuz || !bootloader_file || !config_file) {
197 error("Must specify all input files\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700198 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700199 }
200
Bill Richardsona08b5c92010-06-30 21:59:43 -0700201 bp = (blob_t *)Malloc(sizeof(blob_t));
202 if (!bp) {
203 error("Couldn't allocate bytes for blob_t.\n");
204 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700205 }
Bill Richardsona08b5c92010-06-30 21:59:43 -0700206 bp->kernel_version = version;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700207
208 /* Read the config file */
Bill Richardson249677d2010-06-23 11:16:37 -0700209 Debug("Reading %s\n", config_file);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700210 config_buf = ReadFile(config_file, &config_size);
211 if (!config_buf)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700212 return 0;
Bill Richardson249677d2010-06-23 11:16:37 -0700213 Debug(" config file size=0x%" PRIx64 "\n", config_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700214 if (CROS_CONFIG_SIZE <= config_size) { /* need room for trailing '\0' */
215 error("Config file %s is too large (>= %d bytes)\n",
216 config_file, CROS_CONFIG_SIZE);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700217 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700218 }
219 /* Replace newlines with spaces */
220 for (i = 0; i < config_size; i++)
221 if ('\n' == config_buf[i])
222 config_buf[i] = ' ';
223
224 /* Read the bootloader */
Bill Richardson249677d2010-06-23 11:16:37 -0700225 Debug("Reading %s\n", bootloader_file);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700226 bootloader_buf = ReadFile(bootloader_file, &bootloader_size);
227 if (!bootloader_buf)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700228 return 0;
Bill Richardson249677d2010-06-23 11:16:37 -0700229 Debug(" bootloader file size=0x%" PRIx64 "\n", bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700230
231 /* Read the kernel */
Bill Richardson249677d2010-06-23 11:16:37 -0700232 Debug("Reading %s\n", vmlinuz);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700233 kernel_buf = ReadFile(vmlinuz, &kernel_size);
234 if (!kernel_buf)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700235 return 0;
Bill Richardson249677d2010-06-23 11:16:37 -0700236 Debug(" kernel file size=0x%" PRIx64 "\n", kernel_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700237 if (!kernel_size) {
238 error("Empty kernel file\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700239 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700240 }
241
242 /* The first part of vmlinuz is a header, followed by a real-mode
243 * boot stub. We only want the 32-bit part. */
244 lh = (struct linux_kernel_header *)kernel_buf;
245 kernel32_start = (lh->setup_sects + 1) << 9;
246 if (kernel32_start >= kernel_size) {
247 error("Malformed kernel\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700248 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700249 }
250 kernel32_size = kernel_size - kernel32_start;
Bill Richardson249677d2010-06-23 11:16:37 -0700251 Debug(" kernel32_start=0x%" PRIx64 "\n", kernel32_start);
252 Debug(" kernel32_size=0x%" PRIx64 "\n", kernel32_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700253
254 /* Allocate and zero the blob we need. */
Bill Richardsona08b5c92010-06-30 21:59:43 -0700255 bp->blob_size = roundup(kernel32_size, CROS_ALIGN) +
Randall Spangler7d6898d2010-06-11 09:22:13 -0700256 CROS_CONFIG_SIZE +
257 CROS_PARAMS_SIZE +
258 roundup(bootloader_size, CROS_ALIGN);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700259 blob = (uint8_t *)Malloc(bp->blob_size);
260 Debug("blob_size=0x%" PRIx64 "\n", bp->blob_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700261 if (!blob) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700262 error("Couldn't allocate %ld bytes.\n", bp->blob_size);
263 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700264 }
Bill Richardsona08b5c92010-06-30 21:59:43 -0700265 Memset(blob, 0, bp->blob_size);
266 bp->blob = blob;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700267
268 /* Copy the 32-bit kernel. */
Bill Richardson249677d2010-06-23 11:16:37 -0700269 Debug("kernel goes at blob+=0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700270 if (kernel32_size)
271 Memcpy(blob + now, kernel_buf + kernel32_start, kernel32_size);
272 now += roundup(now + kernel32_size, CROS_ALIGN);
273
Bill Richardson249677d2010-06-23 11:16:37 -0700274 Debug("config goes at blob+0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700275 /* Find the load address of the commandline. We'll need it later. */
276 cmdline_addr = CROS_32BIT_ENTRY_ADDR + now +
277 find_cmdline_start((char *)config_buf, config_size);
Bill Richardson249677d2010-06-23 11:16:37 -0700278 Debug(" cmdline_addr=0x%" PRIx64 "\n", cmdline_addr);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700279
280 /* Copy the config. */
281 if (config_size)
282 Memcpy(blob + now, config_buf, config_size);
283 now += CROS_CONFIG_SIZE;
284
285 /* The zeropage data is next. Overlay the linux_kernel_header onto it, and
286 * tweak a few fields. */
Bill Richardson249677d2010-06-23 11:16:37 -0700287 Debug("params goes at blob+=0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700288 params = (struct linux_kernel_params *)(blob + now);
289 Memcpy(&(params->setup_sects), &(lh->setup_sects),
290 sizeof(*lh) - offsetof(struct linux_kernel_header, setup_sects));
291 params->boot_flag = 0;
292 params->ramdisk_image = 0; /* we don't support initrd */
293 params->ramdisk_size = 0;
294 params->type_of_loader = 0xff;
295 params->cmd_line_ptr = cmdline_addr;
296 now += CROS_PARAMS_SIZE;
297
298 /* Finally, append the bootloader. Remember where it will load in
299 * memory, too. */
Bill Richardson249677d2010-06-23 11:16:37 -0700300 Debug("bootloader goes at blob+=0x%" PRIx64 "\n", now);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700301 bp->bootloader_address = CROS_32BIT_ENTRY_ADDR + now;
302 bp->bootloader_size = roundup(bootloader_size, CROS_ALIGN);
303 Debug(" bootloader_address=0x%" PRIx64 "\n", bp->bootloader_address);
304 Debug(" bootloader_size=0x%" PRIx64 "\n", bp->bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700305 if (bootloader_size)
306 Memcpy(blob + now, bootloader_buf, bootloader_size);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700307 now += bp->bootloader_size;
Bill Richardson249677d2010-06-23 11:16:37 -0700308 Debug("end of blob is 0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700309
310 /* Free input buffers */
311 Free(kernel_buf);
312 Free(config_buf);
313 Free(bootloader_buf);
314
Bill Richardsona08b5c92010-06-30 21:59:43 -0700315 /* Success */
316 return bp;
317}
318
319
320/* Pull the blob_t stuff out of a prepacked kernel blob file */
321static blob_t *OldBlob(const char* filename) {
322 FILE* fp;
323 blob_t *bp;
324 struct stat statbuf;
325 VbKeyBlockHeader* key_block;
326 VbKernelPreambleHeader* preamble;
327 uint64_t now = 0;
328 uint8_t buf[DEFAULT_PADDING];
329
330 if (!filename) {
331 error("Must specify prepacked blob to read\n");
332 return 0;
333 }
334
335 if (0 != stat(filename, &statbuf)) {
336 error("unable to stat %s: %s\n", filename, strerror(errno));
337 return 0;
338 }
339
340 Debug("%s size is 0x%" PRIx64 "\n", filename, statbuf.st_size);
341 if (statbuf.st_size < DEFAULT_PADDING) {
342 error("%s is too small to be a valid kernel blob\n");
343 return 0;
344 }
345
346 Debug("Reading %s\n", filename);
347 fp = fopen(filename, "rb");
348 if (!fp) {
349 error("Unable to open file %s: %s\n", filename, strerror(errno));
350 return 0;
351 }
352
353 if (1 != fread(buf, sizeof(buf), 1, fp)) {
354 error("Unable to read header from %s: %s\n", filename, strerror(errno));
355 fclose(fp);
356 return 0;
357 }
358
359 /* Skip the key block */
360 key_block = (VbKeyBlockHeader*)buf;
361 Debug("Keyblock is 0x%" PRIx64 " bytes\n", key_block->key_block_size);
362 now += key_block->key_block_size;
363 if (now > statbuf.st_size) {
364 error("key_block_size advances past the end of the blob\n");
365 return 0;
366 }
367
368 /* Skip the preamble */
369 preamble = (VbKernelPreambleHeader*)(buf + now);
370 Debug("Preamble is 0x%" PRIx64 " bytes\n", preamble->preamble_size);
371 now += preamble->preamble_size;
372 if (now > statbuf.st_size) {
373 error("preamble_size advances past the end of the blob\n");
374 return 0;
375 }
376
377 /* Go find the kernel blob */
378 Debug("kernel blob is at offset 0x%" PRIx64 "\n", now);
379 if (0 != fseek(fp, now, SEEK_SET)) {
380 error("Unable to seek to 0x%" PRIx64 " in %s: %s\n", now, filename,
381 strerror(errno));
382 fclose(fp);
383 return 0;
384 }
385
386 /* Remember what we've got */
387 bp = (blob_t *)Malloc(sizeof(blob_t));
388 if (!bp) {
389 error("Couldn't allocate bytes for blob_t.\n");
390 fclose(fp);
391 return 0;
392 }
393
394 bp->kernel_version = preamble->kernel_version;
395 bp->bootloader_address = preamble->bootloader_address;
396 bp->bootloader_size = preamble->bootloader_size;
397 bp->blob_size = preamble->body_signature.data_size;
398
399 Debug(" kernel_version = %d\n", bp->kernel_version);
400 Debug(" bootloader_address = 0x%" PRIx64 "\n", bp->bootloader_address);
401 Debug(" bootloader_size = 0x%" PRIx64 "\n", bp->bootloader_size);
402 Debug(" blob_size = 0x%" PRIx64 "\n", bp->blob_size);
403
404 bp->blob = (uint8_t *)Malloc(bp->blob_size);
405 if (!bp->blob) {
406 error("Couldn't allocate 0x%" PRIx64 " bytes for blob_t.\n", bp->blob_size);
407 fclose(fp);
408 Free(bp);
409 return 0;
410 }
411
412 /* read it in */
413 if (1 != fread(bp->blob, bp->blob_size, 1, fp)) {
414 error("Unable to read kernel blob from %s: %s\n", filename, strerror(errno));
415 fclose(fp);
416 Free(bp);
417 return 0;
418 }
419
420 /* done */
421 fclose(fp);
422
423 return bp;
424}
425
426
427/* Pack a .kernel */
428static int Pack(const char* outfile, const char* keyblock_file,
429 const char* signprivate, blob_t *bp, uint64_t pad,
430 int vblockonly) {
431 VbPrivateKey* signing_key;
432 VbSignature* body_sig;
433 VbKernelPreambleHeader* preamble;
434 VbKeyBlockHeader* key_block;
435 uint64_t key_block_size;
436 FILE* f;
437 uint64_t i;
438
439 if (!outfile) {
440 error("Must specify output filename\n");
441 return 1;
442 }
443 if (!keyblock_file || !signprivate) {
444 error("Must specify all keys\n");
445 return 1;
446 }
447 if (!bp) {
448 error("Refusing to pack invalid kernel blob\n");
449 return 1;
450 }
451
452 /* Read the key block and private key */
453 key_block = (VbKeyBlockHeader*)ReadFile(keyblock_file, &key_block_size);
454 if (!key_block) {
455 error("Error reading key block.\n");
456 return 1;
457 }
458 if (pad < key_block->key_block_size) {
459 error("Pad too small\n");
460 return 1;
461 }
462
Bill Richardsonabf05502010-07-01 10:22:06 -0700463 signing_key = PrivateKeyRead(signprivate);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700464 if (!signing_key) {
465 error("Error reading signing key.\n");
466 return 1;
467 }
468
Randall Spangler7d6898d2010-06-11 09:22:13 -0700469 /* Sign the kernel data */
Bill Richardsona08b5c92010-06-30 21:59:43 -0700470 body_sig = CalculateSignature(bp->blob, bp->blob_size, signing_key);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700471 if (!body_sig) {
472 error("Error calculating body signature\n");
473 return 1;
474 }
475
476 /* Create preamble */
Bill Richardsona08b5c92010-06-30 21:59:43 -0700477 preamble = CreateKernelPreamble(bp->kernel_version,
Randall Spangler7d6898d2010-06-11 09:22:13 -0700478 CROS_32BIT_ENTRY_ADDR,
Bill Richardsona08b5c92010-06-30 21:59:43 -0700479 bp->bootloader_address,
480 bp->bootloader_size,
Randall Spangler7d6898d2010-06-11 09:22:13 -0700481 body_sig,
482 pad - key_block_size,
483 signing_key);
484 if (!preamble) {
485 error("Error creating preamble.\n");
486 return 1;
487 }
488
489 /* Write the output file */
Bill Richardson249677d2010-06-23 11:16:37 -0700490 Debug("writing %s...\n", outfile);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700491 f = fopen(outfile, "wb");
492 if (!f) {
493 error("Can't open output file %s\n", outfile);
494 return 1;
495 }
Bill Richardson249677d2010-06-23 11:16:37 -0700496 Debug("0x%" PRIx64 " bytes of key_block\n", key_block_size);
497 Debug("0x%" PRIx64 " bytes of preamble\n", preamble->preamble_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700498 i = ((1 != fwrite(key_block, key_block_size, 1, f)) ||
Bill Richardsona08b5c92010-06-30 21:59:43 -0700499 (1 != fwrite(preamble, preamble->preamble_size, 1, f)));
Randall Spangler7d6898d2010-06-11 09:22:13 -0700500 if (i) {
501 error("Can't write output file %s\n", outfile);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700502 fclose(f);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700503 unlink(outfile);
504 return 1;
505 }
506
Bill Richardsona08b5c92010-06-30 21:59:43 -0700507 if (!vblockonly) {
508 Debug("0x%" PRIx64 " bytes of blob\n", bp->blob_size);
509 i = (1 != fwrite(bp->blob, bp->blob_size, 1, f));
510 if (i) {
511 error("Can't write output file %s\n", outfile);
512 fclose(f);
513 unlink(outfile);
514 return 1;
515 }
516 }
517
518 fclose(f);
519
Randall Spangler7d6898d2010-06-11 09:22:13 -0700520 /* Success */
521 return 0;
522}
523
524
525static int Verify(const char* infile, const char* signpubkey) {
526
527 VbKeyBlockHeader* key_block;
528 VbKernelPreambleHeader* preamble;
529 VbPublicKey* data_key;
530 VbPublicKey* sign_key;
531 RSAPublicKey* rsa;
532 uint8_t* blob;
533 uint64_t blob_size;
534 uint64_t now = 0;
535
536 if (!infile || !signpubkey) {
537 error("Must specify filename and signpubkey\n");
538 return 1;
539 }
540
541 /* Read public signing key */
542 sign_key = PublicKeyRead(signpubkey);
543 if (!sign_key) {
544 error("Error reading signpubkey.\n");
545 return 1;
546 }
547
548 /* Read blob */
549 blob = ReadFile(infile, &blob_size);
550 if (!blob) {
551 error("Error reading input file\n");
552 return 1;
553 }
554
555 /* Verify key block */
556 key_block = (VbKeyBlockHeader*)blob;
Randall Spangler729b8722010-06-11 11:16:20 -0700557 if (0 != KeyBlockVerify(key_block, blob_size, sign_key)) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700558 error("Error verifying key block.\n");
559 return 1;
560 }
561 Free(sign_key);
562 now += key_block->key_block_size;
563
564 printf("Key block:\n");
565 data_key = &key_block->data_key;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700566 printf(" Size: 0x%" PRIx64 "\n", key_block->key_block_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700567 printf(" Data key algorithm: %" PRIu64 " %s\n", data_key->algorithm,
568 (data_key->algorithm < kNumAlgorithms ?
569 algo_strings[data_key->algorithm] : "(invalid)"));
570 printf(" Data key version: %" PRIu64 "\n", data_key->key_version);
571 printf(" Flags: %" PRIu64 "\n", key_block->key_block_flags);
572
573 rsa = PublicKeyToRSA(&key_block->data_key);
574 if (!rsa) {
575 error("Error parsing data key.\n");
576 return 1;
577 }
578
579 /* Verify preamble */
580 preamble = (VbKernelPreambleHeader*)(blob + now);
581 if (0 != VerifyKernelPreamble2(preamble, blob_size - now, rsa)) {
582 error("Error verifying preamble.\n");
583 return 1;
584 }
585 now += preamble->preamble_size;
586
587 printf("Preamble:\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700588 printf(" Size: 0x%" PRIx64 "\n", preamble->preamble_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700589 printf(" Header version: %" PRIu32 ".%" PRIu32"\n",
590 preamble->header_version_major, preamble->header_version_minor);
591 printf(" Kernel version: %" PRIu64 "\n", preamble->kernel_version);
Bill Richardson249677d2010-06-23 11:16:37 -0700592 printf(" Body load address: 0x%" PRIx64 "\n", preamble->body_load_address);
593 printf(" Body size: 0x%" PRIx64 "\n",
Randall Spangler7d6898d2010-06-11 09:22:13 -0700594 preamble->body_signature.data_size);
Bill Richardson249677d2010-06-23 11:16:37 -0700595 printf(" Bootloader address: 0x%" PRIx64 "\n", preamble->bootloader_address);
596 printf(" Bootloader size: 0x%" PRIx64 "\n", preamble->bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700597
598 /* Verify body */
599 if (0 != VerifyData(blob + now, &preamble->body_signature, rsa)) {
600 error("Error verifying kernel body.\n");
601 return 1;
602 }
603 printf("Body verification succeeded.\n");
604 return 0;
605}
606
607
608int main(int argc, char* argv[]) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700609 char* filename = NULL;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700610 char* oldfile = NULL;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700611 char* key_block_file = NULL;
612 char* signpubkey = NULL;
613 char* signprivate = NULL;
614 uint64_t version = 0;
615 char* vmlinuz = NULL;
616 char* bootloader = NULL;
617 char* config_file = NULL;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700618 int vblockonly = 0;
619 uint64_t pad = DEFAULT_PADDING;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700620 int mode = 0;
621 int parse_error = 0;
622 char* e;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700623 int i,r;
624 blob_t *bp;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700625
Bill Richardsona08b5c92010-06-30 21:59:43 -0700626
627 char *progname = strrchr(argv[0], '/');
628 if (progname)
629 progname++;
630 else
631 progname = argv[0];
632
633 while ((i = getopt_long(argc, argv, ":", long_opts, NULL)) != -1) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700634 switch (i) {
635 case '?':
636 /* Unhandled option */
Randall Spangler7d6898d2010-06-11 09:22:13 -0700637 parse_error = 1;
638 break;
639
640 case OPT_MODE_PACK:
Bill Richardsona08b5c92010-06-30 21:59:43 -0700641 case OPT_MODE_REPACK:
Randall Spangler7d6898d2010-06-11 09:22:13 -0700642 case OPT_MODE_VERIFY:
643 mode = i;
644 filename = optarg;
645 break;
646
Bill Richardsona08b5c92010-06-30 21:59:43 -0700647 case OPT_OLDBLOB:
648 oldfile = optarg;
649 break;
650
Randall Spangler7d6898d2010-06-11 09:22:13 -0700651 case OPT_KEYBLOCK:
652 key_block_file = optarg;
653 break;
654
655 case OPT_SIGNPUBKEY:
656 signpubkey = optarg;
657 break;
658
659 case OPT_SIGNPRIVATE:
660 signprivate = optarg;
661 break;
662
663 case OPT_VMLINUZ:
664 vmlinuz = optarg;
665 break;
666
667 case OPT_BOOTLOADER:
668 bootloader = optarg;
669 break;
670
671 case OPT_CONFIG:
672 config_file = optarg;
673 break;
674
Bill Richardsona08b5c92010-06-30 21:59:43 -0700675 case OPT_VBLOCKONLY:
676 vblockonly = 1;
677 break;
678
Randall Spangler7d6898d2010-06-11 09:22:13 -0700679 case OPT_VERSION:
680 version = strtoul(optarg, &e, 0);
681 if (!*optarg || (e && *e)) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700682 fprintf(stderr, "Invalid --version\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700683 parse_error = 1;
684 }
685 break;
686
687 case OPT_PAD:
688 pad = strtoul(optarg, &e, 0);
689 if (!*optarg || (e && *e)) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700690 fprintf(stderr, "Invalid --pad\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700691 parse_error = 1;
692 }
693 break;
694 }
695 }
696
697 if (parse_error)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700698 return PrintHelp(progname);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700699
700 switch(mode) {
701 case OPT_MODE_PACK:
Bill Richardsona08b5c92010-06-30 21:59:43 -0700702 bp = NewBlob(version, vmlinuz, bootloader, config_file);
703 if (!bp)
704 return 1;
705 r = Pack(filename, key_block_file, signprivate, bp, pad, vblockonly);
706 FreeBlob(bp);
707 return r;
708
709 case OPT_MODE_REPACK:
710 bp = OldBlob(oldfile);
711 if (!bp)
712 return 1;
713 r = Pack(filename, key_block_file, signprivate, bp, pad, vblockonly);
714 FreeBlob(bp);
715 return r;
716
Randall Spangler7d6898d2010-06-11 09:22:13 -0700717 case OPT_MODE_VERIFY:
718 return Verify(filename, signpubkey);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700719
Randall Spangler7d6898d2010-06-11 09:22:13 -0700720 default:
Bill Richardsona08b5c92010-06-30 21:59:43 -0700721 fprintf(stderr,
722 "You must specify a mode: --pack, --repack or --verify\n");
723 return PrintHelp(progname);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700724 }
725}