blob: 81b9e445d93478bb5926337cf88da4fe94ec8132 [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
8#include <getopt.h>
9#include <inttypes.h> /* For PRIu64 */
Bill Richardson249677d2010-06-23 11:16:37 -070010#include <stdarg.h>
Randall Spangler7d6898d2010-06-11 09:22:13 -070011#include <stddef.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15
16#include "cryptolib.h"
17#include "host_common.h"
18#include "kernel_blob.h"
19#include "vboot_common.h"
20
21
Bill Richardson249677d2010-06-23 11:16:37 -070022/* Global opt */
23static int opt_debug = 0;
24
25
Randall Spangler7d6898d2010-06-11 09:22:13 -070026/* Command line options */
27enum {
28 OPT_MODE_PACK = 1000,
29 OPT_MODE_VERIFY,
30 OPT_KEYBLOCK,
31 OPT_SIGNPUBKEY,
32 OPT_SIGNPRIVATE,
33 OPT_VERSION,
34 OPT_VMLINUZ,
35 OPT_BOOTLOADER,
36 OPT_CONFIG,
37 OPT_PAD,
38};
39
40static struct option long_opts[] = {
41 {"pack", 1, 0, OPT_MODE_PACK },
42 {"verify", 1, 0, OPT_MODE_VERIFY },
43 {"keyblock", 1, 0, OPT_KEYBLOCK },
44 {"signpubkey", 1, 0, OPT_SIGNPUBKEY },
45 {"signprivate", 1, 0, OPT_SIGNPRIVATE },
46 {"version", 1, 0, OPT_VERSION },
47 {"vmlinuz", 1, 0, OPT_VMLINUZ },
48 {"bootloader", 1, 0, OPT_BOOTLOADER },
49 {"config", 1, 0, OPT_CONFIG },
50 {"pad", 1, 0, OPT_PAD },
Bill Richardson249677d2010-06-23 11:16:37 -070051 {"debug", 0, &opt_debug, 1 },
Randall Spangler7d6898d2010-06-11 09:22:13 -070052 {NULL, 0, 0, 0}
53};
54
55
56/* Print help and return error */
57static int PrintHelp(void) {
58
59 puts("vbutil_kernel - Verified boot key block utility\n"
60 "\n"
61 "Usage: vbutil_kernel <--pack|--verify> <file> [OPTIONS]\n"
62 "\n"
63 "For '--pack <file>', required OPTIONS are:\n"
64 " --keyblock <file> Key block in .keyblock format\n"
65 " --signprivate <file> Signing private key in .pem format\n"
66 " --version <number> Kernel version\n"
67 " --vmlinuz <file> Linux kernel image\n"
68 " --bootloader <file> Bootloader stub\n"
69 " --config <file> Config file\n"
70 "Optional OPTIONS are:\n"
71 " --pad <number> Padding size in bytes\n"
72 "\n"
73 "For '--verify <file>', required OPTIONS are:\n"
74 " --signpubkey <file> Signing public key in .vbpubk format\n"
75 "");
76 return 1;
77}
78
Bill Richardson249677d2010-06-23 11:16:37 -070079static void Debug(const char *format, ...) {
80 if (!opt_debug)
81 return;
82
83 va_list ap;
84 va_start(ap, format);
85 fprintf(stderr, "DEBUG: ");
86 vfprintf(stderr, format, ap);
87 va_end(ap);
88}
89
Randall Spangler7d6898d2010-06-11 09:22:13 -070090
91/* Return the smallest integral multiple of [alignment] that is equal
92 * to or greater than [val]. Used to determine the number of
93 * pages/sectors/blocks/whatever needed to contain [val]
94 * items/bytes/etc. */
95static uint64_t roundup(uint64_t val, uint64_t alignment) {
96 uint64_t rem = val % alignment;
97 if ( rem )
98 return val + (alignment - rem);
99 return val;
100}
101
102
103/* Match regexp /\b--\b/ to delimit the start of the kernel commandline. If we
104 * don't find one, we'll use the whole thing. */
105static unsigned int find_cmdline_start(char *input, unsigned int max_len) {
106 int start = 0;
107 int i;
108 for(i = 0; i < max_len - 1 && input[i]; i++) {
109 if ('-' == input[i] && '-' == input[i + 1]) { /* found a "--" */
110 if ((i == 0 || ' ' == input[i - 1]) && /* nothing before it */
111 (i + 2 >= max_len || ' ' == input[i+2])) { /* nothing after it */
112 start = i+2; /* note: hope there's a trailing '\0' */
113 break;
114 }
115 }
116 }
117 while(' ' == input[start]) /* skip leading spaces */
118 start++;
119
120 return start;
121}
122
123
124/* Pack a .kernel */
125static int Pack(const char* outfile, const char* keyblock_file,
126 const char* signprivate, uint64_t version,
127 const char* vmlinuz, const char* bootloader_file,
128 const char* config_file, uint64_t pad) {
129
130 struct linux_kernel_header *lh = 0;
131 struct linux_kernel_params *params = 0;
132 VbPrivateKey* signing_key;
133 VbSignature* body_sig;
134 VbKernelPreambleHeader* preamble;
135 VbKeyBlockHeader* key_block;
136 uint64_t key_block_size;
137 uint8_t* config_buf;
138 uint64_t config_size;
139 uint8_t* bootloader_buf;
140 uint64_t bootloader_size;
141 uint64_t bootloader_mem_start;
142 uint64_t bootloader_mem_size;
143 uint8_t* kernel_buf;
144 uint64_t kernel_size;
145 uint64_t kernel32_start = 0;
146 uint64_t kernel32_size = 0;
147 uint32_t cmdline_addr;
148 uint8_t* blob = NULL;
149 uint64_t blob_size;
150 uint64_t now = 0;
151 FILE* f;
152 uint64_t i;
153
154 if (!outfile) {
155 error("Must specify output filename\n");
156 return 1;
157 }
158 if (!keyblock_file || !signprivate) {
159 error("Must specify all keys\n");
160 return 1;
161 }
162 if (!vmlinuz || !bootloader_file || !config_file) {
163 error("Must specify all input files\n");
164 return 1;
165 }
166
167 /* Read the key block and private key */
168 key_block = (VbKeyBlockHeader*)ReadFile(keyblock_file, &key_block_size);
169 if (!key_block) {
170 error("Error reading key block.\n");
171 return 1;
172 }
173 if (pad < key_block->key_block_size) {
174 error("Pad too small\n");
175 return 1;
176 }
177
178 signing_key = PrivateKeyRead(signprivate, key_block->data_key.algorithm);
179 if (!signing_key) {
180 error("Error reading signing key.\n");
181 return 1;
182 }
183
184 /* Read the config file */
Bill Richardson249677d2010-06-23 11:16:37 -0700185 Debug("Reading %s\n", config_file);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700186 config_buf = ReadFile(config_file, &config_size);
187 if (!config_buf)
188 return 1;
Bill Richardson249677d2010-06-23 11:16:37 -0700189 Debug(" config file size=0x%" PRIx64 "\n", config_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700190 if (CROS_CONFIG_SIZE <= config_size) { /* need room for trailing '\0' */
191 error("Config file %s is too large (>= %d bytes)\n",
192 config_file, CROS_CONFIG_SIZE);
193 return 1;
194 }
195 /* Replace newlines with spaces */
196 for (i = 0; i < config_size; i++)
197 if ('\n' == config_buf[i])
198 config_buf[i] = ' ';
199
200 /* Read the bootloader */
Bill Richardson249677d2010-06-23 11:16:37 -0700201 Debug("Reading %s\n", bootloader_file);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700202 bootloader_buf = ReadFile(bootloader_file, &bootloader_size);
203 if (!bootloader_buf)
204 return 1;
Bill Richardson249677d2010-06-23 11:16:37 -0700205 Debug(" bootloader file size=0x%" PRIx64 "\n", bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700206
207 /* Read the kernel */
Bill Richardson249677d2010-06-23 11:16:37 -0700208 Debug("Reading %s\n", vmlinuz);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700209 kernel_buf = ReadFile(vmlinuz, &kernel_size);
210 if (!kernel_buf)
211 return 1;
Bill Richardson249677d2010-06-23 11:16:37 -0700212 Debug(" kernel file size=0x%" PRIx64 "\n", kernel_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700213 if (!kernel_size) {
214 error("Empty kernel file\n");
215 return 1;
216 }
217
218 /* The first part of vmlinuz is a header, followed by a real-mode
219 * boot stub. We only want the 32-bit part. */
220 lh = (struct linux_kernel_header *)kernel_buf;
221 kernel32_start = (lh->setup_sects + 1) << 9;
222 if (kernel32_start >= kernel_size) {
223 error("Malformed kernel\n");
224 return 1;
225 }
226 kernel32_size = kernel_size - kernel32_start;
Bill Richardson249677d2010-06-23 11:16:37 -0700227 Debug(" kernel32_start=0x%" PRIx64 "\n", kernel32_start);
228 Debug(" kernel32_size=0x%" PRIx64 "\n", kernel32_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700229
230 /* Allocate and zero the blob we need. */
231 blob_size = roundup(kernel32_size, CROS_ALIGN) +
232 CROS_CONFIG_SIZE +
233 CROS_PARAMS_SIZE +
234 roundup(bootloader_size, CROS_ALIGN);
235 blob = (uint8_t *)Malloc(blob_size);
Bill Richardson249677d2010-06-23 11:16:37 -0700236 Debug("blob_size=0x%" PRIx64 "\n", blob_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700237 if (!blob) {
238 error("Couldn't allocate %ld bytes.\n", blob_size);
239 return 1;
240 }
241 Memset(blob, 0, blob_size);
242
243 /* Copy the 32-bit kernel. */
Bill Richardson249677d2010-06-23 11:16:37 -0700244 Debug("kernel goes at blob+=0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700245 if (kernel32_size)
246 Memcpy(blob + now, kernel_buf + kernel32_start, kernel32_size);
247 now += roundup(now + kernel32_size, CROS_ALIGN);
248
Bill Richardson249677d2010-06-23 11:16:37 -0700249 Debug("config goes at blob+0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700250 /* Find the load address of the commandline. We'll need it later. */
251 cmdline_addr = CROS_32BIT_ENTRY_ADDR + now +
252 find_cmdline_start((char *)config_buf, config_size);
Bill Richardson249677d2010-06-23 11:16:37 -0700253 Debug(" cmdline_addr=0x%" PRIx64 "\n", cmdline_addr);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700254
255 /* Copy the config. */
256 if (config_size)
257 Memcpy(blob + now, config_buf, config_size);
258 now += CROS_CONFIG_SIZE;
259
260 /* The zeropage data is next. Overlay the linux_kernel_header onto it, and
261 * tweak a few fields. */
Bill Richardson249677d2010-06-23 11:16:37 -0700262 Debug("params goes at blob+=0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700263 params = (struct linux_kernel_params *)(blob + now);
264 Memcpy(&(params->setup_sects), &(lh->setup_sects),
265 sizeof(*lh) - offsetof(struct linux_kernel_header, setup_sects));
266 params->boot_flag = 0;
267 params->ramdisk_image = 0; /* we don't support initrd */
268 params->ramdisk_size = 0;
269 params->type_of_loader = 0xff;
270 params->cmd_line_ptr = cmdline_addr;
271 now += CROS_PARAMS_SIZE;
272
273 /* Finally, append the bootloader. Remember where it will load in
274 * memory, too. */
Bill Richardson249677d2010-06-23 11:16:37 -0700275 Debug("bootloader goes at blob+=0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700276 bootloader_mem_start = CROS_32BIT_ENTRY_ADDR + now;
277 bootloader_mem_size = roundup(bootloader_size, CROS_ALIGN);
Bill Richardson249677d2010-06-23 11:16:37 -0700278 Debug(" bootloader_mem_start=0x%" PRIx64 "\n", bootloader_mem_start);
279 Debug(" bootloader_mem_size=0x%" PRIx64 "\n", bootloader_mem_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700280 if (bootloader_size)
281 Memcpy(blob + now, bootloader_buf, bootloader_size);
282 now += bootloader_mem_size;
Bill Richardson249677d2010-06-23 11:16:37 -0700283 Debug("end of blob is 0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700284
285 /* Free input buffers */
286 Free(kernel_buf);
287 Free(config_buf);
288 Free(bootloader_buf);
289
290 /* Sign the kernel data */
291 body_sig = CalculateSignature(blob, blob_size, signing_key);
292 if (!body_sig) {
293 error("Error calculating body signature\n");
294 return 1;
295 }
296
297 /* Create preamble */
298 preamble = CreateKernelPreamble(version,
299 CROS_32BIT_ENTRY_ADDR,
300 bootloader_mem_start,
301 bootloader_mem_size,
302 body_sig,
303 pad - key_block_size,
304 signing_key);
305 if (!preamble) {
306 error("Error creating preamble.\n");
307 return 1;
308 }
309
310 /* Write the output file */
Bill Richardson249677d2010-06-23 11:16:37 -0700311 Debug("writing %s...\n", outfile);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700312 f = fopen(outfile, "wb");
313 if (!f) {
314 error("Can't open output file %s\n", outfile);
315 return 1;
316 }
Bill Richardson249677d2010-06-23 11:16:37 -0700317 Debug("0x%" PRIx64 " bytes of key_block\n", key_block_size);
318 Debug("0x%" PRIx64 " bytes of preamble\n", preamble->preamble_size);
319 Debug("0x%" PRIx64 " bytes of blob\n", blob_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700320 i = ((1 != fwrite(key_block, key_block_size, 1, f)) ||
321 (1 != fwrite(preamble, preamble->preamble_size, 1, f)) ||
322 (1 != fwrite(blob, blob_size, 1, f)));
323 fclose(f);
324 if (i) {
325 error("Can't write output file %s\n", outfile);
326 unlink(outfile);
327 return 1;
328 }
329
330 /* Success */
331 return 0;
332}
333
334
335static int Verify(const char* infile, const char* signpubkey) {
336
337 VbKeyBlockHeader* key_block;
338 VbKernelPreambleHeader* preamble;
339 VbPublicKey* data_key;
340 VbPublicKey* sign_key;
341 RSAPublicKey* rsa;
342 uint8_t* blob;
343 uint64_t blob_size;
344 uint64_t now = 0;
345
346 if (!infile || !signpubkey) {
347 error("Must specify filename and signpubkey\n");
348 return 1;
349 }
350
351 /* Read public signing key */
352 sign_key = PublicKeyRead(signpubkey);
353 if (!sign_key) {
354 error("Error reading signpubkey.\n");
355 return 1;
356 }
357
358 /* Read blob */
359 blob = ReadFile(infile, &blob_size);
360 if (!blob) {
361 error("Error reading input file\n");
362 return 1;
363 }
364
365 /* Verify key block */
366 key_block = (VbKeyBlockHeader*)blob;
Randall Spangler729b8722010-06-11 11:16:20 -0700367 if (0 != KeyBlockVerify(key_block, blob_size, sign_key)) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700368 error("Error verifying key block.\n");
369 return 1;
370 }
371 Free(sign_key);
372 now += key_block->key_block_size;
373
374 printf("Key block:\n");
375 data_key = &key_block->data_key;
376 printf(" Size: %" PRIu64 "\n", key_block->key_block_size);
377 printf(" Data key algorithm: %" PRIu64 " %s\n", data_key->algorithm,
378 (data_key->algorithm < kNumAlgorithms ?
379 algo_strings[data_key->algorithm] : "(invalid)"));
380 printf(" Data key version: %" PRIu64 "\n", data_key->key_version);
381 printf(" Flags: %" PRIu64 "\n", key_block->key_block_flags);
382
383 rsa = PublicKeyToRSA(&key_block->data_key);
384 if (!rsa) {
385 error("Error parsing data key.\n");
386 return 1;
387 }
388
389 /* Verify preamble */
390 preamble = (VbKernelPreambleHeader*)(blob + now);
391 if (0 != VerifyKernelPreamble2(preamble, blob_size - now, rsa)) {
392 error("Error verifying preamble.\n");
393 return 1;
394 }
395 now += preamble->preamble_size;
396
397 printf("Preamble:\n");
398 printf(" Size: %" PRIu64 "\n", preamble->preamble_size);
399 printf(" Header version: %" PRIu32 ".%" PRIu32"\n",
400 preamble->header_version_major, preamble->header_version_minor);
401 printf(" Kernel version: %" PRIu64 "\n", preamble->kernel_version);
Bill Richardson249677d2010-06-23 11:16:37 -0700402 printf(" Body load address: 0x%" PRIx64 "\n", preamble->body_load_address);
403 printf(" Body size: 0x%" PRIx64 "\n",
Randall Spangler7d6898d2010-06-11 09:22:13 -0700404 preamble->body_signature.data_size);
Bill Richardson249677d2010-06-23 11:16:37 -0700405 printf(" Bootloader address: 0x%" PRIx64 "\n", preamble->bootloader_address);
406 printf(" Bootloader size: 0x%" PRIx64 "\n", preamble->bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700407
408 /* Verify body */
409 if (0 != VerifyData(blob + now, &preamble->body_signature, rsa)) {
410 error("Error verifying kernel body.\n");
411 return 1;
412 }
413 printf("Body verification succeeded.\n");
414 return 0;
415}
416
417
418int main(int argc, char* argv[]) {
419
420 char* filename = NULL;
421 char* key_block_file = NULL;
422 char* signpubkey = NULL;
423 char* signprivate = NULL;
424 uint64_t version = 0;
425 char* vmlinuz = NULL;
426 char* bootloader = NULL;
427 char* config_file = NULL;
428 uint64_t pad = 65536;
429 int mode = 0;
430 int parse_error = 0;
431 char* e;
432 int i;
433
434 while ((i = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
435 switch (i) {
436 case '?':
437 /* Unhandled option */
438 printf("Unknown option\n");
439 parse_error = 1;
440 break;
441
442 case OPT_MODE_PACK:
443 case OPT_MODE_VERIFY:
444 mode = i;
445 filename = optarg;
446 break;
447
448 case OPT_KEYBLOCK:
449 key_block_file = optarg;
450 break;
451
452 case OPT_SIGNPUBKEY:
453 signpubkey = optarg;
454 break;
455
456 case OPT_SIGNPRIVATE:
457 signprivate = optarg;
458 break;
459
460 case OPT_VMLINUZ:
461 vmlinuz = optarg;
462 break;
463
464 case OPT_BOOTLOADER:
465 bootloader = optarg;
466 break;
467
468 case OPT_CONFIG:
469 config_file = optarg;
470 break;
471
472 case OPT_VERSION:
473 version = strtoul(optarg, &e, 0);
474 if (!*optarg || (e && *e)) {
475 printf("Invalid --version\n");
476 parse_error = 1;
477 }
478 break;
479
480 case OPT_PAD:
481 pad = strtoul(optarg, &e, 0);
482 if (!*optarg || (e && *e)) {
483 printf("Invalid --pad\n");
484 parse_error = 1;
485 }
486 break;
487 }
488 }
489
490 if (parse_error)
491 return PrintHelp();
492
493 switch(mode) {
494 case OPT_MODE_PACK:
495 return Pack(filename, key_block_file, signprivate, version, vmlinuz,
496 bootloader, config_file, pad);
497 case OPT_MODE_VERIFY:
498 return Verify(filename, signpubkey);
499 default:
500 printf("Must specify a mode.\n");
501 return PrintHelp();
502 }
503}