OLD | NEW |
1 /* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 1 /* 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 | 2 * Use of this source code is governed by a BSD-style license that can be |
3 * found in the LICENSE file. | 3 * found in the LICENSE file. |
4 * | 4 * |
5 * Verified boot kernel utility | 5 * Verified boot kernel utility |
6 */ | 6 */ |
7 | 7 |
8 #include <getopt.h> | 8 #include <getopt.h> |
9 #include <inttypes.h> /* For PRIu64 */ | 9 #include <inttypes.h> /* For PRIu64 */ |
| 10 #include <stdarg.h> |
10 #include <stddef.h> | 11 #include <stddef.h> |
11 #include <stdio.h> | 12 #include <stdio.h> |
12 #include <stdlib.h> | 13 #include <stdlib.h> |
13 #include <unistd.h> | 14 #include <unistd.h> |
14 | 15 |
15 #include "cryptolib.h" | 16 #include "cryptolib.h" |
16 #include "host_common.h" | 17 #include "host_common.h" |
17 #include "kernel_blob.h" | 18 #include "kernel_blob.h" |
18 #include "vboot_common.h" | 19 #include "vboot_common.h" |
19 | 20 |
20 | 21 |
| 22 /* Global opt */ |
| 23 static int opt_debug = 0; |
| 24 |
| 25 |
21 /* Command line options */ | 26 /* Command line options */ |
22 enum { | 27 enum { |
23 OPT_MODE_PACK = 1000, | 28 OPT_MODE_PACK = 1000, |
24 OPT_MODE_VERIFY, | 29 OPT_MODE_VERIFY, |
25 OPT_KEYBLOCK, | 30 OPT_KEYBLOCK, |
26 OPT_SIGNPUBKEY, | 31 OPT_SIGNPUBKEY, |
27 OPT_SIGNPRIVATE, | 32 OPT_SIGNPRIVATE, |
28 OPT_VERSION, | 33 OPT_VERSION, |
29 OPT_VMLINUZ, | 34 OPT_VMLINUZ, |
30 OPT_BOOTLOADER, | 35 OPT_BOOTLOADER, |
31 OPT_CONFIG, | 36 OPT_CONFIG, |
32 OPT_PAD, | 37 OPT_PAD, |
33 }; | 38 }; |
34 | 39 |
35 static struct option long_opts[] = { | 40 static struct option long_opts[] = { |
36 {"pack", 1, 0, OPT_MODE_PACK }, | 41 {"pack", 1, 0, OPT_MODE_PACK }, |
37 {"verify", 1, 0, OPT_MODE_VERIFY }, | 42 {"verify", 1, 0, OPT_MODE_VERIFY }, |
38 {"keyblock", 1, 0, OPT_KEYBLOCK }, | 43 {"keyblock", 1, 0, OPT_KEYBLOCK }, |
39 {"signpubkey", 1, 0, OPT_SIGNPUBKEY }, | 44 {"signpubkey", 1, 0, OPT_SIGNPUBKEY }, |
40 {"signprivate", 1, 0, OPT_SIGNPRIVATE }, | 45 {"signprivate", 1, 0, OPT_SIGNPRIVATE }, |
41 {"version", 1, 0, OPT_VERSION }, | 46 {"version", 1, 0, OPT_VERSION }, |
42 {"vmlinuz", 1, 0, OPT_VMLINUZ }, | 47 {"vmlinuz", 1, 0, OPT_VMLINUZ }, |
43 {"bootloader", 1, 0, OPT_BOOTLOADER }, | 48 {"bootloader", 1, 0, OPT_BOOTLOADER }, |
44 {"config", 1, 0, OPT_CONFIG }, | 49 {"config", 1, 0, OPT_CONFIG }, |
45 {"pad", 1, 0, OPT_PAD }, | 50 {"pad", 1, 0, OPT_PAD }, |
| 51 {"debug", 0, &opt_debug, 1 }, |
46 {NULL, 0, 0, 0} | 52 {NULL, 0, 0, 0} |
47 }; | 53 }; |
48 | 54 |
49 | 55 |
50 /* Print help and return error */ | 56 /* Print help and return error */ |
51 static int PrintHelp(void) { | 57 static int PrintHelp(void) { |
52 | 58 |
53 puts("vbutil_kernel - Verified boot key block utility\n" | 59 puts("vbutil_kernel - Verified boot key block utility\n" |
54 "\n" | 60 "\n" |
55 "Usage: vbutil_kernel <--pack|--verify> <file> [OPTIONS]\n" | 61 "Usage: vbutil_kernel <--pack|--verify> <file> [OPTIONS]\n" |
56 "\n" | 62 "\n" |
57 "For '--pack <file>', required OPTIONS are:\n" | 63 "For '--pack <file>', required OPTIONS are:\n" |
58 " --keyblock <file> Key block in .keyblock format\n" | 64 " --keyblock <file> Key block in .keyblock format\n" |
59 " --signprivate <file> Signing private key in .pem format\n" | 65 " --signprivate <file> Signing private key in .pem format\n" |
60 " --version <number> Kernel version\n" | 66 " --version <number> Kernel version\n" |
61 " --vmlinuz <file> Linux kernel image\n" | 67 " --vmlinuz <file> Linux kernel image\n" |
62 " --bootloader <file> Bootloader stub\n" | 68 " --bootloader <file> Bootloader stub\n" |
63 " --config <file> Config file\n" | 69 " --config <file> Config file\n" |
64 "Optional OPTIONS are:\n" | 70 "Optional OPTIONS are:\n" |
65 " --pad <number> Padding size in bytes\n" | 71 " --pad <number> Padding size in bytes\n" |
66 "\n" | 72 "\n" |
67 "For '--verify <file>', required OPTIONS are:\n" | 73 "For '--verify <file>', required OPTIONS are:\n" |
68 " --signpubkey <file> Signing public key in .vbpubk format\n" | 74 " --signpubkey <file> Signing public key in .vbpubk format\n" |
69 ""); | 75 ""); |
70 return 1; | 76 return 1; |
71 } | 77 } |
72 | 78 |
| 79 static 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 |
73 | 90 |
74 /* Return the smallest integral multiple of [alignment] that is equal | 91 /* Return the smallest integral multiple of [alignment] that is equal |
75 * to or greater than [val]. Used to determine the number of | 92 * to or greater than [val]. Used to determine the number of |
76 * pages/sectors/blocks/whatever needed to contain [val] | 93 * pages/sectors/blocks/whatever needed to contain [val] |
77 * items/bytes/etc. */ | 94 * items/bytes/etc. */ |
78 static uint64_t roundup(uint64_t val, uint64_t alignment) { | 95 static uint64_t roundup(uint64_t val, uint64_t alignment) { |
79 uint64_t rem = val % alignment; | 96 uint64_t rem = val % alignment; |
80 if ( rem ) | 97 if ( rem ) |
81 return val + (alignment - rem); | 98 return val + (alignment - rem); |
82 return val; | 99 return val; |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
158 return 1; | 175 return 1; |
159 } | 176 } |
160 | 177 |
161 signing_key = PrivateKeyRead(signprivate, key_block->data_key.algorithm); | 178 signing_key = PrivateKeyRead(signprivate, key_block->data_key.algorithm); |
162 if (!signing_key) { | 179 if (!signing_key) { |
163 error("Error reading signing key.\n"); | 180 error("Error reading signing key.\n"); |
164 return 1; | 181 return 1; |
165 } | 182 } |
166 | 183 |
167 /* Read the config file */ | 184 /* Read the config file */ |
| 185 Debug("Reading %s\n", config_file); |
168 config_buf = ReadFile(config_file, &config_size); | 186 config_buf = ReadFile(config_file, &config_size); |
169 if (!config_buf) | 187 if (!config_buf) |
170 return 1; | 188 return 1; |
| 189 Debug(" config file size=0x%" PRIx64 "\n", config_size); |
171 if (CROS_CONFIG_SIZE <= config_size) { /* need room for trailing '\0' */ | 190 if (CROS_CONFIG_SIZE <= config_size) { /* need room for trailing '\0' */ |
172 error("Config file %s is too large (>= %d bytes)\n", | 191 error("Config file %s is too large (>= %d bytes)\n", |
173 config_file, CROS_CONFIG_SIZE); | 192 config_file, CROS_CONFIG_SIZE); |
174 return 1; | 193 return 1; |
175 } | 194 } |
176 /* Replace newlines with spaces */ | 195 /* Replace newlines with spaces */ |
177 for (i = 0; i < config_size; i++) | 196 for (i = 0; i < config_size; i++) |
178 if ('\n' == config_buf[i]) | 197 if ('\n' == config_buf[i]) |
179 config_buf[i] = ' '; | 198 config_buf[i] = ' '; |
180 | 199 |
181 /* Read the bootloader */ | 200 /* Read the bootloader */ |
| 201 Debug("Reading %s\n", bootloader_file); |
182 bootloader_buf = ReadFile(bootloader_file, &bootloader_size); | 202 bootloader_buf = ReadFile(bootloader_file, &bootloader_size); |
183 if (!bootloader_buf) | 203 if (!bootloader_buf) |
184 return 1; | 204 return 1; |
| 205 Debug(" bootloader file size=0x%" PRIx64 "\n", bootloader_size); |
185 | 206 |
186 /* Read the kernel */ | 207 /* Read the kernel */ |
| 208 Debug("Reading %s\n", vmlinuz); |
187 kernel_buf = ReadFile(vmlinuz, &kernel_size); | 209 kernel_buf = ReadFile(vmlinuz, &kernel_size); |
188 if (!kernel_buf) | 210 if (!kernel_buf) |
189 return 1; | 211 return 1; |
| 212 Debug(" kernel file size=0x%" PRIx64 "\n", kernel_size); |
190 if (!kernel_size) { | 213 if (!kernel_size) { |
191 error("Empty kernel file\n"); | 214 error("Empty kernel file\n"); |
192 return 1; | 215 return 1; |
193 } | 216 } |
194 | 217 |
195 /* The first part of vmlinuz is a header, followed by a real-mode | 218 /* The first part of vmlinuz is a header, followed by a real-mode |
196 * boot stub. We only want the 32-bit part. */ | 219 * boot stub. We only want the 32-bit part. */ |
197 lh = (struct linux_kernel_header *)kernel_buf; | 220 lh = (struct linux_kernel_header *)kernel_buf; |
198 kernel32_start = (lh->setup_sects + 1) << 9; | 221 kernel32_start = (lh->setup_sects + 1) << 9; |
199 if (kernel32_start >= kernel_size) { | 222 if (kernel32_start >= kernel_size) { |
200 error("Malformed kernel\n"); | 223 error("Malformed kernel\n"); |
201 return 1; | 224 return 1; |
202 } | 225 } |
203 kernel32_size = kernel_size - kernel32_start; | 226 kernel32_size = kernel_size - kernel32_start; |
| 227 Debug(" kernel32_start=0x%" PRIx64 "\n", kernel32_start); |
| 228 Debug(" kernel32_size=0x%" PRIx64 "\n", kernel32_size); |
204 | 229 |
205 /* Allocate and zero the blob we need. */ | 230 /* Allocate and zero the blob we need. */ |
206 blob_size = roundup(kernel32_size, CROS_ALIGN) + | 231 blob_size = roundup(kernel32_size, CROS_ALIGN) + |
207 CROS_CONFIG_SIZE + | 232 CROS_CONFIG_SIZE + |
208 CROS_PARAMS_SIZE + | 233 CROS_PARAMS_SIZE + |
209 roundup(bootloader_size, CROS_ALIGN); | 234 roundup(bootloader_size, CROS_ALIGN); |
210 blob = (uint8_t *)Malloc(blob_size); | 235 blob = (uint8_t *)Malloc(blob_size); |
| 236 Debug("blob_size=0x%" PRIx64 "\n", blob_size); |
211 if (!blob) { | 237 if (!blob) { |
212 error("Couldn't allocate %ld bytes.\n", blob_size); | 238 error("Couldn't allocate %ld bytes.\n", blob_size); |
213 return 1; | 239 return 1; |
214 } | 240 } |
215 Memset(blob, 0, blob_size); | 241 Memset(blob, 0, blob_size); |
216 | 242 |
217 /* Copy the 32-bit kernel. */ | 243 /* Copy the 32-bit kernel. */ |
| 244 Debug("kernel goes at blob+=0x%" PRIx64 "\n", now); |
218 if (kernel32_size) | 245 if (kernel32_size) |
219 Memcpy(blob + now, kernel_buf + kernel32_start, kernel32_size); | 246 Memcpy(blob + now, kernel_buf + kernel32_start, kernel32_size); |
220 now += roundup(now + kernel32_size, CROS_ALIGN); | 247 now += roundup(now + kernel32_size, CROS_ALIGN); |
221 | 248 |
| 249 Debug("config goes at blob+0x%" PRIx64 "\n", now); |
222 /* Find the load address of the commandline. We'll need it later. */ | 250 /* Find the load address of the commandline. We'll need it later. */ |
223 cmdline_addr = CROS_32BIT_ENTRY_ADDR + now + | 251 cmdline_addr = CROS_32BIT_ENTRY_ADDR + now + |
224 find_cmdline_start((char *)config_buf, config_size); | 252 find_cmdline_start((char *)config_buf, config_size); |
| 253 Debug(" cmdline_addr=0x%" PRIx64 "\n", cmdline_addr); |
225 | 254 |
226 /* Copy the config. */ | 255 /* Copy the config. */ |
227 if (config_size) | 256 if (config_size) |
228 Memcpy(blob + now, config_buf, config_size); | 257 Memcpy(blob + now, config_buf, config_size); |
229 now += CROS_CONFIG_SIZE; | 258 now += CROS_CONFIG_SIZE; |
230 | 259 |
231 /* The zeropage data is next. Overlay the linux_kernel_header onto it, and | 260 /* The zeropage data is next. Overlay the linux_kernel_header onto it, and |
232 * tweak a few fields. */ | 261 * tweak a few fields. */ |
| 262 Debug("params goes at blob+=0x%" PRIx64 "\n", now); |
233 params = (struct linux_kernel_params *)(blob + now); | 263 params = (struct linux_kernel_params *)(blob + now); |
234 Memcpy(&(params->setup_sects), &(lh->setup_sects), | 264 Memcpy(&(params->setup_sects), &(lh->setup_sects), |
235 sizeof(*lh) - offsetof(struct linux_kernel_header, setup_sects)); | 265 sizeof(*lh) - offsetof(struct linux_kernel_header, setup_sects)); |
236 params->boot_flag = 0; | 266 params->boot_flag = 0; |
237 params->ramdisk_image = 0; /* we don't support initrd */ | 267 params->ramdisk_image = 0; /* we don't support initrd */ |
238 params->ramdisk_size = 0; | 268 params->ramdisk_size = 0; |
239 params->type_of_loader = 0xff; | 269 params->type_of_loader = 0xff; |
240 params->cmd_line_ptr = cmdline_addr; | 270 params->cmd_line_ptr = cmdline_addr; |
241 now += CROS_PARAMS_SIZE; | 271 now += CROS_PARAMS_SIZE; |
242 | 272 |
243 /* Finally, append the bootloader. Remember where it will load in | 273 /* Finally, append the bootloader. Remember where it will load in |
244 * memory, too. */ | 274 * memory, too. */ |
| 275 Debug("bootloader goes at blob+=0x%" PRIx64 "\n", now); |
245 bootloader_mem_start = CROS_32BIT_ENTRY_ADDR + now; | 276 bootloader_mem_start = CROS_32BIT_ENTRY_ADDR + now; |
246 bootloader_mem_size = roundup(bootloader_size, CROS_ALIGN); | 277 bootloader_mem_size = roundup(bootloader_size, CROS_ALIGN); |
| 278 Debug(" bootloader_mem_start=0x%" PRIx64 "\n", bootloader_mem_start); |
| 279 Debug(" bootloader_mem_size=0x%" PRIx64 "\n", bootloader_mem_size); |
247 if (bootloader_size) | 280 if (bootloader_size) |
248 Memcpy(blob + now, bootloader_buf, bootloader_size); | 281 Memcpy(blob + now, bootloader_buf, bootloader_size); |
249 now += bootloader_mem_size; | 282 now += bootloader_mem_size; |
| 283 Debug("end of blob is 0x%" PRIx64 "\n", now); |
250 | 284 |
251 /* Free input buffers */ | 285 /* Free input buffers */ |
252 Free(kernel_buf); | 286 Free(kernel_buf); |
253 Free(config_buf); | 287 Free(config_buf); |
254 Free(bootloader_buf); | 288 Free(bootloader_buf); |
255 | 289 |
256 /* Sign the kernel data */ | 290 /* Sign the kernel data */ |
257 body_sig = CalculateSignature(blob, blob_size, signing_key); | 291 body_sig = CalculateSignature(blob, blob_size, signing_key); |
258 if (!body_sig) { | 292 if (!body_sig) { |
259 error("Error calculating body signature\n"); | 293 error("Error calculating body signature\n"); |
260 return 1; | 294 return 1; |
261 } | 295 } |
262 | 296 |
263 /* Create preamble */ | 297 /* Create preamble */ |
264 preamble = CreateKernelPreamble(version, | 298 preamble = CreateKernelPreamble(version, |
265 CROS_32BIT_ENTRY_ADDR, | 299 CROS_32BIT_ENTRY_ADDR, |
266 bootloader_mem_start, | 300 bootloader_mem_start, |
267 bootloader_mem_size, | 301 bootloader_mem_size, |
268 body_sig, | 302 body_sig, |
269 pad - key_block_size, | 303 pad - key_block_size, |
270 signing_key); | 304 signing_key); |
271 if (!preamble) { | 305 if (!preamble) { |
272 error("Error creating preamble.\n"); | 306 error("Error creating preamble.\n"); |
273 return 1; | 307 return 1; |
274 } | 308 } |
275 | 309 |
276 /* Write the output file */ | 310 /* Write the output file */ |
| 311 Debug("writing %s...\n", outfile); |
277 f = fopen(outfile, "wb"); | 312 f = fopen(outfile, "wb"); |
278 if (!f) { | 313 if (!f) { |
279 error("Can't open output file %s\n", outfile); | 314 error("Can't open output file %s\n", outfile); |
280 return 1; | 315 return 1; |
281 } | 316 } |
| 317 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); |
282 i = ((1 != fwrite(key_block, key_block_size, 1, f)) || | 320 i = ((1 != fwrite(key_block, key_block_size, 1, f)) || |
283 (1 != fwrite(preamble, preamble->preamble_size, 1, f)) || | 321 (1 != fwrite(preamble, preamble->preamble_size, 1, f)) || |
284 (1 != fwrite(blob, blob_size, 1, f))); | 322 (1 != fwrite(blob, blob_size, 1, f))); |
285 fclose(f); | 323 fclose(f); |
286 if (i) { | 324 if (i) { |
287 error("Can't write output file %s\n", outfile); | 325 error("Can't write output file %s\n", outfile); |
288 unlink(outfile); | 326 unlink(outfile); |
289 return 1; | 327 return 1; |
290 } | 328 } |
291 | 329 |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
354 error("Error verifying preamble.\n"); | 392 error("Error verifying preamble.\n"); |
355 return 1; | 393 return 1; |
356 } | 394 } |
357 now += preamble->preamble_size; | 395 now += preamble->preamble_size; |
358 | 396 |
359 printf("Preamble:\n"); | 397 printf("Preamble:\n"); |
360 printf(" Size: %" PRIu64 "\n", preamble->preamble_size); | 398 printf(" Size: %" PRIu64 "\n", preamble->preamble_size); |
361 printf(" Header version: %" PRIu32 ".%" PRIu32"\n", | 399 printf(" Header version: %" PRIu32 ".%" PRIu32"\n", |
362 preamble->header_version_major, preamble->header_version_minor); | 400 preamble->header_version_major, preamble->header_version_minor); |
363 printf(" Kernel version: %" PRIu64 "\n", preamble->kernel_version); | 401 printf(" Kernel version: %" PRIu64 "\n", preamble->kernel_version); |
364 printf(" Body load address: %" PRIu64 "\n", preamble->body_load_address); | 402 printf(" Body load address: 0x%" PRIx64 "\n", preamble->body_load_address); |
365 printf(" Body size: %" PRIu64 "\n", | 403 printf(" Body size: 0x%" PRIx64 "\n", |
366 preamble->body_signature.data_size); | 404 preamble->body_signature.data_size); |
367 printf(" Bootloader address: %" PRIu64 "\n", preamble->bootloader_address); | 405 printf(" Bootloader address: 0x%" PRIx64 "\n", preamble->bootloader_address)
; |
368 printf(" Bootloader size: %" PRIu64 "\n", preamble->bootloader_size); | 406 printf(" Bootloader size: 0x%" PRIx64 "\n", preamble->bootloader_size); |
369 | 407 |
370 /* Verify body */ | 408 /* Verify body */ |
371 if (0 != VerifyData(blob + now, &preamble->body_signature, rsa)) { | 409 if (0 != VerifyData(blob + now, &preamble->body_signature, rsa)) { |
372 error("Error verifying kernel body.\n"); | 410 error("Error verifying kernel body.\n"); |
373 return 1; | 411 return 1; |
374 } | 412 } |
375 printf("Body verification succeeded.\n"); | 413 printf("Body verification succeeded.\n"); |
376 return 0; | 414 return 0; |
377 } | 415 } |
378 | 416 |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
456 case OPT_MODE_PACK: | 494 case OPT_MODE_PACK: |
457 return Pack(filename, key_block_file, signprivate, version, vmlinuz, | 495 return Pack(filename, key_block_file, signprivate, version, vmlinuz, |
458 bootloader, config_file, pad); | 496 bootloader, config_file, pad); |
459 case OPT_MODE_VERIFY: | 497 case OPT_MODE_VERIFY: |
460 return Verify(filename, signpubkey); | 498 return Verify(filename, signpubkey); |
461 default: | 499 default: |
462 printf("Must specify a mode.\n"); | 500 printf("Must specify a mode.\n"); |
463 return PrintHelp(); | 501 return PrintHelp(); |
464 } | 502 } |
465 } | 503 } |
OLD | NEW |