Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 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 |
| 3 | * found in the LICENSE file. |
| 4 | * |
| 5 | * Developer file-signing utility |
| 6 | */ |
| 7 | |
| 8 | #include <errno.h> |
| 9 | #include <getopt.h> |
| 10 | #include <inttypes.h> /* For PRIu64 */ |
| 11 | #include <stdarg.h> |
| 12 | #include <stddef.h> |
| 13 | #include <stdio.h> |
| 14 | #include <stdlib.h> |
| 15 | #include <string.h> |
| 16 | #include <sys/stat.h> |
| 17 | #include <sys/types.h> |
| 18 | #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 | |
| 26 | /* Global opt */ |
| 27 | static int opt_debug = 0; |
| 28 | |
| 29 | /* Command line options */ |
| 30 | enum { |
| 31 | OPT_MODE_SIGN = 1000, |
| 32 | OPT_MODE_VERIFY, |
| 33 | OPT_KEYBLOCK, |
| 34 | OPT_SIGNPRIVATE, |
| 35 | OPT_VBLOCK, |
| 36 | }; |
| 37 | |
| 38 | static struct option long_opts[] = { |
| 39 | {"sign", 1, 0, OPT_MODE_SIGN }, |
| 40 | {"verify", 1, 0, OPT_MODE_VERIFY }, |
| 41 | {"keyblock", 1, 0, OPT_KEYBLOCK }, |
| 42 | {"signprivate", 1, 0, OPT_SIGNPRIVATE }, |
| 43 | {"vblock", 1, 0, OPT_VBLOCK }, |
| 44 | {"debug", 0, &opt_debug, 1 }, |
| 45 | {NULL, 0, 0, 0} |
| 46 | }; |
| 47 | |
| 48 | |
| 49 | /* Print help and return error */ |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 50 | static int PrintHelp(const char *progname) { |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 51 | fprintf(stderr, |
| 52 | "This program is used to sign and verify developer-mode files\n"); |
| 53 | fprintf(stderr, |
| 54 | "\n" |
| 55 | "Usage: %s --sign <file> [PARAMETERS]\n" |
| 56 | "\n" |
| 57 | " Required parameters:\n" |
| 58 | " --keyblock <file> Key block in .keyblock format\n" |
| 59 | " --signprivate <file>" |
| 60 | " Private key to sign file data, in .vbprivk format\n" |
| 61 | " --vblock <file> Output signature in .vblock format\n" |
| 62 | "\n", |
| 63 | progname); |
| 64 | fprintf(stderr, |
| 65 | "OR\n\n" |
| 66 | "Usage: %s --verify <file> [PARAMETERS]\n" |
| 67 | "\n" |
| 68 | " Required parameters:\n" |
| 69 | " --vblock <file> Signature file in .vblock format\n" |
Bill Richardson | 0697e3f | 2010-08-17 16:58:46 -0700 | [diff] [blame] | 70 | "\n" |
| 71 | " Optional parameters:\n" |
| 72 | " --keyblock <file>" |
| 73 | " Extract .keyblock to file if verification succeeds\n" |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 74 | "\n", |
| 75 | progname); |
| 76 | return 1; |
| 77 | } |
| 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 | |
| 90 | |
| 91 | /* Sign a file. We'll reuse the same structs used to sign kernels, to avoid |
| 92 | having to declare yet another one for just this purpose. */ |
| 93 | static int Sign(const char* filename, const char* keyblock_file, |
| 94 | const char* signprivate_file, const char* outfile) { |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 95 | uint8_t* file_data; |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 96 | uint64_t file_size; |
| 97 | VbKeyBlockHeader* key_block; |
| 98 | uint64_t key_block_size; |
| 99 | VbPrivateKey* signing_key; |
| 100 | VbSignature* body_sig; |
| 101 | VbKernelPreambleHeader* preamble; |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 102 | FILE* output_fp; |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 103 | |
| 104 | /* Read the file that we're going to sign. */ |
| 105 | file_data = ReadFile(filename, &file_size); |
| 106 | if (!file_data) { |
| 107 | error("Error reading file to sign.\n"); |
| 108 | return 1; |
| 109 | } |
| 110 | |
| 111 | /* Get the key block and read the private key corresponding to it. */ |
| 112 | key_block = (VbKeyBlockHeader*)ReadFile(keyblock_file, &key_block_size); |
| 113 | if (!key_block) { |
| 114 | error("Error reading key block.\n"); |
| 115 | return 1; |
| 116 | } |
| 117 | signing_key = PrivateKeyRead(signprivate_file); |
| 118 | if (!signing_key) { |
| 119 | error("Error reading signing key.\n"); |
| 120 | return 1; |
| 121 | } |
| 122 | |
| 123 | /* Sign the file data */ |
| 124 | body_sig = CalculateSignature(file_data, file_size, signing_key); |
| 125 | if (!body_sig) { |
| 126 | error("Error calculating body signature\n"); |
| 127 | return 1; |
| 128 | } |
| 129 | |
| 130 | /* Create preamble */ |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 131 | preamble = CreateKernelPreamble((uint64_t)0, |
| 132 | (uint64_t)0, |
| 133 | (uint64_t)0, |
| 134 | (uint64_t)0, |
| 135 | body_sig, |
| 136 | (uint64_t)0, |
| 137 | signing_key); |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 138 | if (!preamble) { |
| 139 | error("Error creating preamble.\n"); |
| 140 | return 1; |
| 141 | } |
| 142 | |
| 143 | /* Write the output file */ |
| 144 | Debug("writing %s...\n", outfile); |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 145 | output_fp = fopen(outfile, "wb"); |
| 146 | if (!output_fp) { |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 147 | error("Can't open output file %s\n", outfile); |
| 148 | return 1; |
| 149 | } |
| 150 | Debug("0x%" PRIx64 " bytes of key_block\n", key_block_size); |
| 151 | Debug("0x%" PRIx64 " bytes of preamble\n", preamble->preamble_size); |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 152 | if ((1 != fwrite(key_block, key_block_size, 1, output_fp)) || |
| 153 | (1 != fwrite(preamble, preamble->preamble_size, 1, output_fp))) { |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 154 | error("Can't write output file %s\n", outfile); |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 155 | fclose(output_fp); |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 156 | unlink(outfile); |
| 157 | return 1; |
| 158 | } |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 159 | fclose(output_fp); |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 160 | |
| 161 | /* Done */ |
| 162 | Free(preamble); |
| 163 | Free(body_sig); |
| 164 | Free(signing_key); |
| 165 | Free(key_block); |
| 166 | Free(file_data); |
| 167 | |
| 168 | /* Success */ |
| 169 | return 0; |
| 170 | } |
| 171 | |
Bill Richardson | 0697e3f | 2010-08-17 16:58:46 -0700 | [diff] [blame] | 172 | static int Verify(const char* filename, const char* vblock_file, |
| 173 | const char* keyblock_file) { |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 174 | uint8_t* file_data; |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 175 | uint64_t file_size; |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 176 | uint8_t* buf; |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 177 | uint64_t buf_size; |
| 178 | VbKeyBlockHeader* key_block; |
| 179 | VbKernelPreambleHeader* preamble; |
| 180 | VbPublicKey* data_key; |
| 181 | RSAPublicKey* rsa; |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 182 | uint64_t current_buf_offset = 0; |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 183 | |
| 184 | /* Read the file that we're going to verify. */ |
| 185 | file_data = ReadFile(filename, &file_size); |
| 186 | if (!file_data) { |
| 187 | error("Error reading file to sign.\n"); |
| 188 | return 1; |
| 189 | } |
| 190 | |
| 191 | /* Read the vblock that we're going to use on it */ |
| 192 | buf = ReadFile(vblock_file, &buf_size); |
| 193 | if (!buf) { |
| 194 | error("Error reading vblock_file.\n"); |
| 195 | return 1; |
| 196 | } |
| 197 | |
| 198 | /* Find the key block */ |
| 199 | key_block = (VbKeyBlockHeader*)buf; |
| 200 | Debug("Keyblock is 0x%" PRIx64 " bytes\n", key_block->key_block_size); |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 201 | current_buf_offset += key_block->key_block_size; |
| 202 | if (current_buf_offset > buf_size) { |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 203 | error("key_block_size advances past the end of the buffer\n"); |
| 204 | return 1; |
| 205 | } |
| 206 | |
| 207 | /* Find the preamble */ |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 208 | preamble = (VbKernelPreambleHeader*)(buf + current_buf_offset); |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 209 | Debug("Preamble is 0x%" PRIx64 " bytes\n", preamble->preamble_size); |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 210 | current_buf_offset += preamble->preamble_size; |
| 211 | if (current_buf_offset > buf_size ) { |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 212 | error("preamble_size advances past the end of the buffer\n"); |
| 213 | return 1; |
| 214 | } |
| 215 | |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 216 | Debug("Current buf offset is at 0x%" PRIx64 " bytes\n", current_buf_offset); |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 217 | |
Randall Spangler | 138acfe | 2010-08-17 15:45:21 -0700 | [diff] [blame] | 218 | /* Check the key block (hash only) */ |
Bill Richardson | 4be36c4 | 2010-08-19 08:27:31 -0700 | [diff] [blame] | 219 | if (0 != KeyBlockVerify(key_block, key_block->key_block_size, NULL, 1)) { |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 220 | error("Error verifying key block.\n"); |
| 221 | return 1; |
| 222 | } |
| 223 | |
| 224 | printf("Key block:\n"); |
| 225 | data_key = &key_block->data_key; |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 226 | printf(" Size: 0x%" PRIx64 "\n", key_block->key_block_size); |
| 227 | printf(" Data key algorithm: %" PRIu64 " %s\n", data_key->algorithm, |
| 228 | (data_key->algorithm < kNumAlgorithms ? |
| 229 | algo_strings[data_key->algorithm] : "(invalid)")); |
| 230 | printf(" Data key version: %" PRIu64 "\n", data_key->key_version); |
| 231 | printf(" Flags: %" PRIu64 "\n", key_block->key_block_flags); |
| 232 | |
| 233 | |
| 234 | /* Verify preamble */ |
| 235 | rsa = PublicKeyToRSA(&key_block->data_key); |
| 236 | if (!rsa) { |
| 237 | error("Error parsing data key.\n"); |
| 238 | return 1; |
| 239 | } |
Bill Richardson | 4be36c4 | 2010-08-19 08:27:31 -0700 | [diff] [blame] | 240 | if (0 != VerifyKernelPreamble(preamble, preamble->preamble_size, rsa)) { |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 241 | error("Error verifying preamble.\n"); |
| 242 | return 1; |
| 243 | } |
| 244 | |
| 245 | printf("Preamble:\n"); |
| 246 | printf(" Size: 0x%" PRIx64 "\n", preamble->preamble_size); |
| 247 | printf(" Header version: %" PRIu32 ".%" PRIu32"\n", |
| 248 | preamble->header_version_major, preamble->header_version_minor); |
| 249 | printf(" Kernel version: %" PRIu64 "\n", preamble->kernel_version); |
| 250 | printf(" Body load address: 0x%" PRIx64 "\n", preamble->body_load_address); |
| 251 | printf(" Body size: 0x%" PRIx64 "\n", |
| 252 | preamble->body_signature.data_size); |
| 253 | printf(" Bootloader address: 0x%" PRIx64 "\n", |
| 254 | preamble->bootloader_address); |
| 255 | printf(" Bootloader size: 0x%" PRIx64 "\n", preamble->bootloader_size); |
| 256 | |
| 257 | /* Verify body */ |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 258 | if (0 != VerifyData(file_data, file_size, &preamble->body_signature, rsa)) { |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 259 | error("Error verifying kernel body.\n"); |
| 260 | return 1; |
| 261 | } |
| 262 | printf("Body verification succeeded.\n"); |
| 263 | |
Bill Richardson | 0697e3f | 2010-08-17 16:58:46 -0700 | [diff] [blame] | 264 | if (keyblock_file) { |
| 265 | if (0 != WriteFile(keyblock_file, key_block, key_block->key_block_size)) { |
| 266 | error("Unable to export keyblock file\n"); |
| 267 | return 1; |
| 268 | } |
| 269 | printf("Key block exported to %s\n", keyblock_file); |
| 270 | } |
| 271 | |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 272 | return 0; |
| 273 | } |
| 274 | |
| 275 | |
| 276 | int main(int argc, char* argv[]) { |
| 277 | char* filename = NULL; |
| 278 | char* keyblock_file = NULL; |
| 279 | char* signprivate_file = NULL; |
| 280 | char* vblock_file = NULL; |
| 281 | int mode = 0; |
| 282 | int parse_error = 0; |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 283 | int option_index; |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 284 | |
| 285 | char *progname = strrchr(argv[0], '/'); |
| 286 | if (progname) |
| 287 | progname++; |
| 288 | else |
| 289 | progname = argv[0]; |
| 290 | |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 291 | while ((option_index = getopt_long(argc, argv, ":", long_opts, NULL)) != -1 && |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 292 | !parse_error) { |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 293 | switch (option_index) { |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 294 | default: |
| 295 | case '?': |
| 296 | /* Unhandled option */ |
| 297 | parse_error = 1; |
| 298 | break; |
| 299 | |
| 300 | case 0: |
| 301 | /* silently handled option */ |
| 302 | break; |
| 303 | |
| 304 | case OPT_MODE_SIGN: |
| 305 | case OPT_MODE_VERIFY: |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 306 | if (mode && (mode != option_index)) { |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 307 | fprintf(stderr, "Only a single mode can be specified\n"); |
| 308 | parse_error = 1; |
| 309 | break; |
| 310 | } |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 311 | mode = option_index; |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 312 | filename = optarg; |
| 313 | break; |
| 314 | |
| 315 | case OPT_KEYBLOCK: |
| 316 | keyblock_file = optarg; |
| 317 | break; |
| 318 | |
| 319 | case OPT_SIGNPRIVATE: |
| 320 | signprivate_file = optarg; |
| 321 | break; |
| 322 | |
| 323 | case OPT_VBLOCK: |
| 324 | vblock_file = optarg; |
| 325 | break; |
| 326 | } |
| 327 | } |
| 328 | |
| 329 | if (parse_error) |
| 330 | return PrintHelp(progname); |
| 331 | |
| 332 | switch(mode) { |
| 333 | case OPT_MODE_SIGN: |
| 334 | if (!keyblock_file || !signprivate_file || !vblock_file) { |
| 335 | fprintf(stderr, "Some required options are missing\n"); |
| 336 | return PrintHelp(progname); |
| 337 | } |
| 338 | return Sign(filename, keyblock_file, signprivate_file, vblock_file); |
| 339 | |
| 340 | case OPT_MODE_VERIFY: |
| 341 | if (!vblock_file) { |
| 342 | fprintf(stderr, "Some required options are missing\n"); |
| 343 | return PrintHelp(progname); |
| 344 | } |
Bill Richardson | 0697e3f | 2010-08-17 16:58:46 -0700 | [diff] [blame] | 345 | return Verify(filename, vblock_file, keyblock_file); |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 346 | |
| 347 | default: |
| 348 | fprintf(stderr, |
| 349 | "You must specify either --sign or --verify\n"); |
| 350 | return PrintHelp(progname); |
| 351 | } |
| 352 | |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 353 | /* NOTREACHED */ |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 354 | return 1; |
| 355 | } |