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" |
| 70 | "\n", |
| 71 | progname); |
| 72 | return 1; |
| 73 | } |
| 74 | |
| 75 | static void Debug(const char *format, ...) { |
| 76 | if (!opt_debug) |
| 77 | return; |
| 78 | |
| 79 | va_list ap; |
| 80 | va_start(ap, format); |
| 81 | fprintf(stderr, "DEBUG: "); |
| 82 | vfprintf(stderr, format, ap); |
| 83 | va_end(ap); |
| 84 | } |
| 85 | |
| 86 | |
| 87 | /* Sign a file. We'll reuse the same structs used to sign kernels, to avoid |
| 88 | having to declare yet another one for just this purpose. */ |
| 89 | static int Sign(const char* filename, const char* keyblock_file, |
| 90 | const char* signprivate_file, const char* outfile) { |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 91 | uint8_t* file_data; |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 92 | uint64_t file_size; |
| 93 | VbKeyBlockHeader* key_block; |
| 94 | uint64_t key_block_size; |
| 95 | VbPrivateKey* signing_key; |
| 96 | VbSignature* body_sig; |
| 97 | VbKernelPreambleHeader* preamble; |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 98 | FILE* output_fp; |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 99 | |
| 100 | /* Read the file that we're going to sign. */ |
| 101 | file_data = ReadFile(filename, &file_size); |
| 102 | if (!file_data) { |
| 103 | error("Error reading file to sign.\n"); |
| 104 | return 1; |
| 105 | } |
| 106 | |
| 107 | /* Get the key block and read the private key corresponding to it. */ |
| 108 | key_block = (VbKeyBlockHeader*)ReadFile(keyblock_file, &key_block_size); |
| 109 | if (!key_block) { |
| 110 | error("Error reading key block.\n"); |
| 111 | return 1; |
| 112 | } |
| 113 | signing_key = PrivateKeyRead(signprivate_file); |
| 114 | if (!signing_key) { |
| 115 | error("Error reading signing key.\n"); |
| 116 | return 1; |
| 117 | } |
| 118 | |
| 119 | /* Sign the file data */ |
| 120 | body_sig = CalculateSignature(file_data, file_size, signing_key); |
| 121 | if (!body_sig) { |
| 122 | error("Error calculating body signature\n"); |
| 123 | return 1; |
| 124 | } |
| 125 | |
| 126 | /* Create preamble */ |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 127 | preamble = CreateKernelPreamble((uint64_t)0, |
| 128 | (uint64_t)0, |
| 129 | (uint64_t)0, |
| 130 | (uint64_t)0, |
| 131 | body_sig, |
| 132 | (uint64_t)0, |
| 133 | signing_key); |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 134 | if (!preamble) { |
| 135 | error("Error creating preamble.\n"); |
| 136 | return 1; |
| 137 | } |
| 138 | |
| 139 | /* Write the output file */ |
| 140 | Debug("writing %s...\n", outfile); |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 141 | output_fp = fopen(outfile, "wb"); |
| 142 | if (!output_fp) { |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 143 | error("Can't open output file %s\n", outfile); |
| 144 | return 1; |
| 145 | } |
| 146 | Debug("0x%" PRIx64 " bytes of key_block\n", key_block_size); |
| 147 | Debug("0x%" PRIx64 " bytes of preamble\n", preamble->preamble_size); |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 148 | if ((1 != fwrite(key_block, key_block_size, 1, output_fp)) || |
| 149 | (1 != fwrite(preamble, preamble->preamble_size, 1, output_fp))) { |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 150 | error("Can't write output file %s\n", outfile); |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 151 | fclose(output_fp); |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 152 | unlink(outfile); |
| 153 | return 1; |
| 154 | } |
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 | |
| 157 | /* Done */ |
| 158 | Free(preamble); |
| 159 | Free(body_sig); |
| 160 | Free(signing_key); |
| 161 | Free(key_block); |
| 162 | Free(file_data); |
| 163 | |
| 164 | /* Success */ |
| 165 | return 0; |
| 166 | } |
| 167 | |
| 168 | static int Verify(const char* filename, const char* vblock_file) { |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 169 | uint8_t* file_data; |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 170 | uint64_t file_size; |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 171 | uint8_t* buf; |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 172 | uint64_t buf_size; |
| 173 | VbKeyBlockHeader* key_block; |
| 174 | VbKernelPreambleHeader* preamble; |
| 175 | VbPublicKey* data_key; |
| 176 | RSAPublicKey* rsa; |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 177 | uint64_t current_buf_offset = 0; |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 178 | |
| 179 | /* Read the file that we're going to verify. */ |
| 180 | file_data = ReadFile(filename, &file_size); |
| 181 | if (!file_data) { |
| 182 | error("Error reading file to sign.\n"); |
| 183 | return 1; |
| 184 | } |
| 185 | |
| 186 | /* Read the vblock that we're going to use on it */ |
| 187 | buf = ReadFile(vblock_file, &buf_size); |
| 188 | if (!buf) { |
| 189 | error("Error reading vblock_file.\n"); |
| 190 | return 1; |
| 191 | } |
| 192 | |
| 193 | /* Find the key block */ |
| 194 | key_block = (VbKeyBlockHeader*)buf; |
| 195 | Debug("Keyblock is 0x%" PRIx64 " bytes\n", key_block->key_block_size); |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 196 | current_buf_offset += key_block->key_block_size; |
| 197 | if (current_buf_offset > buf_size) { |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 198 | error("key_block_size advances past the end of the buffer\n"); |
| 199 | return 1; |
| 200 | } |
| 201 | |
| 202 | /* Find the preamble */ |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 203 | preamble = (VbKernelPreambleHeader*)(buf + current_buf_offset); |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 204 | Debug("Preamble is 0x%" PRIx64 " bytes\n", preamble->preamble_size); |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 205 | current_buf_offset += preamble->preamble_size; |
| 206 | if (current_buf_offset > buf_size ) { |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 207 | error("preamble_size advances past the end of the buffer\n"); |
| 208 | return 1; |
| 209 | } |
| 210 | |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 211 | 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] | 212 | |
Randall Spangler | 138acfe | 2010-08-17 15:45:21 -0700 | [diff] [blame^] | 213 | /* Check the key block (hash only) */ |
| 214 | if (0 != KeyBlockVerify(key_block, file_size, NULL, 1)) { |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 215 | error("Error verifying key block.\n"); |
| 216 | return 1; |
| 217 | } |
| 218 | |
| 219 | printf("Key block:\n"); |
| 220 | data_key = &key_block->data_key; |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 221 | printf(" Size: 0x%" PRIx64 "\n", key_block->key_block_size); |
| 222 | printf(" Data key algorithm: %" PRIu64 " %s\n", data_key->algorithm, |
| 223 | (data_key->algorithm < kNumAlgorithms ? |
| 224 | algo_strings[data_key->algorithm] : "(invalid)")); |
| 225 | printf(" Data key version: %" PRIu64 "\n", data_key->key_version); |
| 226 | printf(" Flags: %" PRIu64 "\n", key_block->key_block_flags); |
| 227 | |
| 228 | |
| 229 | /* Verify preamble */ |
| 230 | rsa = PublicKeyToRSA(&key_block->data_key); |
| 231 | if (!rsa) { |
| 232 | error("Error parsing data key.\n"); |
| 233 | return 1; |
| 234 | } |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 235 | if (0 != VerifyKernelPreamble(preamble, file_size, rsa)) { |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 236 | error("Error verifying preamble.\n"); |
| 237 | return 1; |
| 238 | } |
| 239 | |
| 240 | printf("Preamble:\n"); |
| 241 | printf(" Size: 0x%" PRIx64 "\n", preamble->preamble_size); |
| 242 | printf(" Header version: %" PRIu32 ".%" PRIu32"\n", |
| 243 | preamble->header_version_major, preamble->header_version_minor); |
| 244 | printf(" Kernel version: %" PRIu64 "\n", preamble->kernel_version); |
| 245 | printf(" Body load address: 0x%" PRIx64 "\n", preamble->body_load_address); |
| 246 | printf(" Body size: 0x%" PRIx64 "\n", |
| 247 | preamble->body_signature.data_size); |
| 248 | printf(" Bootloader address: 0x%" PRIx64 "\n", |
| 249 | preamble->bootloader_address); |
| 250 | printf(" Bootloader size: 0x%" PRIx64 "\n", preamble->bootloader_size); |
| 251 | |
| 252 | /* Verify body */ |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 253 | if (0 != VerifyData(file_data, file_size, &preamble->body_signature, rsa)) { |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 254 | error("Error verifying kernel body.\n"); |
| 255 | return 1; |
| 256 | } |
| 257 | printf("Body verification succeeded.\n"); |
| 258 | |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 259 | return 0; |
| 260 | } |
| 261 | |
| 262 | |
| 263 | int main(int argc, char* argv[]) { |
| 264 | char* filename = NULL; |
| 265 | char* keyblock_file = NULL; |
| 266 | char* signprivate_file = NULL; |
| 267 | char* vblock_file = NULL; |
| 268 | int mode = 0; |
| 269 | int parse_error = 0; |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 270 | int option_index; |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 271 | |
| 272 | char *progname = strrchr(argv[0], '/'); |
| 273 | if (progname) |
| 274 | progname++; |
| 275 | else |
| 276 | progname = argv[0]; |
| 277 | |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 278 | while ((option_index = getopt_long(argc, argv, ":", long_opts, NULL)) != -1 && |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 279 | !parse_error) { |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 280 | switch (option_index) { |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 281 | default: |
| 282 | case '?': |
| 283 | /* Unhandled option */ |
| 284 | parse_error = 1; |
| 285 | break; |
| 286 | |
| 287 | case 0: |
| 288 | /* silently handled option */ |
| 289 | break; |
| 290 | |
| 291 | case OPT_MODE_SIGN: |
| 292 | case OPT_MODE_VERIFY: |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 293 | if (mode && (mode != option_index)) { |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 294 | fprintf(stderr, "Only a single mode can be specified\n"); |
| 295 | parse_error = 1; |
| 296 | break; |
| 297 | } |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 298 | mode = option_index; |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 299 | filename = optarg; |
| 300 | break; |
| 301 | |
| 302 | case OPT_KEYBLOCK: |
| 303 | keyblock_file = optarg; |
| 304 | break; |
| 305 | |
| 306 | case OPT_SIGNPRIVATE: |
| 307 | signprivate_file = optarg; |
| 308 | break; |
| 309 | |
| 310 | case OPT_VBLOCK: |
| 311 | vblock_file = optarg; |
| 312 | break; |
| 313 | } |
| 314 | } |
| 315 | |
| 316 | if (parse_error) |
| 317 | return PrintHelp(progname); |
| 318 | |
| 319 | switch(mode) { |
| 320 | case OPT_MODE_SIGN: |
| 321 | if (!keyblock_file || !signprivate_file || !vblock_file) { |
| 322 | fprintf(stderr, "Some required options are missing\n"); |
| 323 | return PrintHelp(progname); |
| 324 | } |
| 325 | return Sign(filename, keyblock_file, signprivate_file, vblock_file); |
| 326 | |
| 327 | case OPT_MODE_VERIFY: |
| 328 | if (!vblock_file) { |
| 329 | fprintf(stderr, "Some required options are missing\n"); |
| 330 | return PrintHelp(progname); |
| 331 | } |
| 332 | return Verify(filename, vblock_file); |
| 333 | |
| 334 | default: |
| 335 | fprintf(stderr, |
| 336 | "You must specify either --sign or --verify\n"); |
| 337 | return PrintHelp(progname); |
| 338 | } |
| 339 | |
Bill Richardson | 83ba6d3 | 2010-08-11 13:23:35 -0700 | [diff] [blame] | 340 | /* NOTREACHED */ |
Bill Richardson | 5aa673c | 2010-08-10 12:20:34 -0700 | [diff] [blame] | 341 | return 1; |
| 342 | } |