blob: 9a4f34b832baace8a70a698a3839a625c9d353c3 [file] [log] [blame]
Gaurav Shah80d129b2010-03-03 17:58:43 -08001// 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//
Gaurav Shahccaa90f2010-03-17 20:40:23 -07005// Utility for manipulating verified boot kernel images.
Gaurav Shah80d129b2010-03-03 17:58:43 -08006//
7
8#include "kernel_utility.h"
9
10#include <errno.h>
11#include <getopt.h>
Gaurav Shaha6fb7a62010-03-04 15:00:50 -080012#include <stdio.h>
Gaurav Shah80d129b2010-03-03 17:58:43 -080013#include <stdint.h> // Needed for UINT16_MAX.
14#include <stdlib.h>
15#include <unistd.h>
16
17#include <iostream>
18
19extern "C" {
20#include "file_keys.h"
21#include "kernel_image.h"
22#include "padding.h"
23#include "rsa_utility.h"
24#include "sha_utility.h"
25#include "utility.h"
26}
27
28extern int errno;
29using std::cerr;
30
31namespace vboot_reference {
32
33KernelUtility::KernelUtility(): image_(NULL),
34 firmware_key_pub_(NULL),
35 header_version_(1),
36 firmware_sign_algorithm_(-1),
37 kernel_sign_algorithm_(-1),
38 kernel_key_version_(-1),
39 kernel_version_(-1),
40 is_generate_(false),
Gaurav Shah528a2c12010-03-18 13:10:10 -070041 is_verify_(false),
42 is_describe_(false){
Gaurav Shah80d129b2010-03-03 17:58:43 -080043 // Populate kernel config options with defaults.
44 options_.version[0] = 1;
45 options_.version[1] = 0;
46 options_.kernel_len = 0;
47 options_.kernel_load_addr = 0;
48 options_.kernel_entry_addr = 0;
49}
50
51KernelUtility::~KernelUtility() {
52 RSAPublicKeyFree(firmware_key_pub_);
53 KernelImageFree(image_);
54}
55
56void KernelUtility::PrintUsage(void) {
57 cerr <<
Gaurav Shah528a2c12010-03-18 13:10:10 -070058 "Utility to generate/verify/describe a verified boot kernel image\n\n"
59 "Usage: kernel_utility <--generate|--verify|--describe> [OPTIONS]\n\n"
Gaurav Shah80d129b2010-03-03 17:58:43 -080060 "For \"--verify\", required OPTIONS are:\n"
61 "--in <infile>\t\t\tVerified boot kernel image to verify.\n"
62 "--firmware_key_pub <pubkeyfile>\tPre-processed public firmware key "
63 "to use for verification.\n\n"
64 "For \"--generate\", required OPTIONS are:\n"
65 "--firmware_key <privkeyfile>\tPrivate firmware signing key file\n"
66 "--kernel_key <privkeyfile>\tPrivate kernel signing key file\n"
67 "--kernel_key_pub <pubkeyfile>\tPre-processed public kernel signing"
68 " key\n"
69 "--firmware_sign_algorithm <algoid>\tSigning algorithm used by "
70 "the firmware\n"
71 "--kernel_sign_algorithm <algoid>\tSigning algorithm to use for kernel\n"
72 "--kernel_key_version <version#>\tKernel signing Key Version#\n"
73 "--kernel_version <version#>\tKernel Version#\n"
74 "--in <infile>\t\tKernel Image to sign\n"
75 "--out <outfile>\t\tOutput file for verified boot Kernel image\n\n"
76 "Optional arguments for \"--generate\" include:\n"
77 "--config_version <version>\n"
78 "--kernel_load_addr <addr>\n"
79 "--kernel_entry_addr <addr>\n\n"
80 "<algoid> (for --*_sign_algorithm) is one of the following:\n";
81 for (int i = 0; i < kNumAlgorithms; i++) {
82 cerr << i << " for " << algo_strings[i] << "\n";
83 }
84 cerr << "\n\n";
85}
86
87bool KernelUtility::ParseCmdLineOptions(int argc, char* argv[]) {
88 int option_index;
89 static struct option long_options[] = {
90 {"firmware_key", 1, 0, 0},
91 {"firmware_key_pub", 1, 0, 0},
92 {"kernel_key", 1, 0, 0},
93 {"kernel_key_pub", 1, 0, 0},
94 {"firmware_sign_algorithm", 1, 0, 0},
95 {"kernel_sign_algorithm", 1, 0, 0},
96 {"kernel_key_version", 1, 0, 0},
97 {"kernel_version", 1, 0, 0},
98 {"in", 1, 0, 0},
99 {"out", 1, 0, 0},
100 {"generate", 0, 0, 0},
101 {"verify", 0, 0, 0},
102 {"config_version", 1, 0, 0},
103 {"kernel_load_addr", 1, 0, 0},
104 {"kernel_entry_addr", 1, 0, 0},
Gaurav Shah528a2c12010-03-18 13:10:10 -0700105 {"describe", 0, 0, 0},
Gaurav Shah80d129b2010-03-03 17:58:43 -0800106 {NULL, 0, 0, 0}
107 };
108 while (1) {
109 int i = getopt_long(argc, argv, "", long_options, &option_index);
110 if (-1 == i) // Done with option processing.
111 break;
112 if ('?' == i) // Invalid option found.
113 return false;
114
115 if (0 == i) {
116 switch (option_index) {
117 case 0: // firmware_key
118 firmware_key_file_ = optarg;
119 break;
120 case 1: // firmware_key_pub
121 firmware_key_pub_file_ = optarg;
122 break;
123 case 2: // kernel_key
124 kernel_key_file_ = optarg;
125 break;
126 case 3: // kernel_key_pub
127 kernel_key_pub_file_ = optarg;
128 break;
129 case 4: // firmware_sign_algorithm
130 errno = 0; // strtol() returns an error via errno
131 firmware_sign_algorithm_ = strtol(optarg,
132 reinterpret_cast<char**>(NULL), 10);
133 if (errno)
134 return false;
135 break;
136 case 5: // kernel_sign_algorithm
137 errno = 0;
138 kernel_sign_algorithm_ = strtol(optarg,
139 reinterpret_cast<char**>(NULL), 10);
140 if (errno)
141 return false;
142 break;
143 case 6: // kernel_key_version
144 errno = 0;
145 kernel_key_version_ = strtol(optarg,
146 reinterpret_cast<char**>(NULL), 10);
147 if (errno)
148 return false;
149 break;
150 case 7: // kernel_version
151 errno = 0;
152 kernel_version_ = strtol(optarg,
153 reinterpret_cast<char**>(NULL), 10);
154 if (errno)
155 return false;
156 break;
157 case 8: // in
158 in_file_ = optarg;
159 break;
160 case 9: // out
161 out_file_ = optarg;
162 break;
163 case 10: // generate
164 is_generate_ = true;
165 break;
166 case 11: // verify
167 is_verify_ = true;
168 break;
169 case 12: // config_version
170 if (2 != sscanf(optarg, "%d.%d", &options_.version[0],
171 &options_.version[1]))
172 return false;
173 break;
174 case 13: // kernel_load_addr
175 errno = 0;
176 options_.kernel_load_addr =
177 strtol(optarg, reinterpret_cast<char**>(NULL), 10);
178 if (errno)
179 return false;
180 break;
181 case 14: // kernel_entry_addr
182 errno = 0;
183 options_.kernel_entry_addr =
184 strtol(optarg, reinterpret_cast<char**>(NULL), 10);
Gaurav Shah80d129b2010-03-03 17:58:43 -0800185 if (errno)
186 return false;
187 break;
Gaurav Shah528a2c12010-03-18 13:10:10 -0700188 case 15: // describe
189 is_describe_ = true;
190 break;
Gaurav Shah80d129b2010-03-03 17:58:43 -0800191 }
192 }
193 }
194 return CheckOptions();
195}
196
197void KernelUtility::OutputSignedImage(void) {
198 if (image_) {
199 if (!WriteKernelImage(out_file_.c_str(), image_)) {
200 cerr << "Couldn't write verified boot kernel image to file "
201 << out_file_ <<".\n";
202 }
203 }
204}
205
Gaurav Shah528a2c12010-03-18 13:10:10 -0700206void KernelUtility::DescribeSignedImage(void) {
207 image_ = ReadKernelImage(in_file_.c_str());
208 if (!image_) {
209 cerr << "Couldn't read kernel image or malformed image.\n";
210 return;
211 }
212 PrintKernelImage(image_);
213}
214
Gaurav Shah80d129b2010-03-03 17:58:43 -0800215bool KernelUtility::GenerateSignedImage(void) {
Gaurav Shah456678b2010-03-10 18:38:45 -0800216 uint64_t kernel_key_pub_len;
Gaurav Shah80d129b2010-03-03 17:58:43 -0800217 image_ = KernelImageNew();
218
219 Memcpy(image_->magic, KERNEL_MAGIC, KERNEL_MAGIC_SIZE);
220
221 // TODO(gauravsh): make this a command line option.
222 image_->header_version = 1;
223 image_->firmware_sign_algorithm = (uint16_t) firmware_sign_algorithm_;
224 // Copy pre-processed public signing key.
225 image_->kernel_sign_algorithm = (uint16_t) kernel_sign_algorithm_;
226 image_->kernel_sign_key = BufferFromFile(kernel_key_pub_file_.c_str(),
227 &kernel_key_pub_len);
228 if (!image_->kernel_sign_key)
229 return false;
230 image_->kernel_key_version = kernel_key_version_;
231
232 // Update header length.
233 image_->header_len = GetKernelHeaderLen(image_);
234
235 // Calculate header checksum.
Gaurav Shah528a2c12010-03-18 13:10:10 -0700236 CalculateKernelHeaderChecksum(image_, image_->header_checksum);
Gaurav Shah80d129b2010-03-03 17:58:43 -0800237
238 image_->kernel_version = kernel_version_;
239 image_->options.version[0] = options_.version[0];
240 image_->options.version[1] = options_.version[1];
Gaurav Shah528a2c12010-03-18 13:10:10 -0700241 // TODO(gauravsh): Add a command line option for this.
242 Memset(image_->options.cmd_line, 0, sizeof(image_->options.cmd_line));
Gaurav Shah80d129b2010-03-03 17:58:43 -0800243 image_->options.kernel_load_addr = options_.kernel_load_addr;
244 image_->options.kernel_entry_addr = options_.kernel_entry_addr;
245 image_->kernel_data = BufferFromFile(in_file_.c_str(),
246 &image_->options.kernel_len);
247 if (!image_)
248 return false;
249 // Generate and add the signatures.
250 if (!AddKernelKeySignature(image_, firmware_key_file_.c_str())) {
Gaurav Shahccaa90f2010-03-17 20:40:23 -0700251 cerr << "Couldn't write key signature to verified boot kernel image.\n";
Gaurav Shah80d129b2010-03-03 17:58:43 -0800252 return false;
253 }
254
255 if (!AddKernelSignature(image_, kernel_key_file_.c_str())) {
Gaurav Shahccaa90f2010-03-17 20:40:23 -0700256 cerr << "Couldn't write firmware signature to verified boot kernel image.\n";
Gaurav Shah80d129b2010-03-03 17:58:43 -0800257 return false;
258 }
259 return true;
260}
261
262bool KernelUtility::VerifySignedImage(void) {
263 int error;
264 firmware_key_pub_ = RSAPublicKeyFromFile(firmware_key_pub_file_.c_str());
265 image_ = ReadKernelImage(in_file_.c_str());
266
267 if (!firmware_key_pub_) {
268 cerr << "Couldn't read pre-processed public root key.\n";
269 return false;
270 }
271
272 if (!image_) {
Gaurav Shahccaa90f2010-03-17 20:40:23 -0700273 cerr << "Couldn't read kernel image or malformed image.\n";
Gaurav Shah80d129b2010-03-03 17:58:43 -0800274 return false;
275 }
276 if (!(error = VerifyKernelImage(firmware_key_pub_, image_, 0)))
277 return true;
278 cerr << VerifyKernelErrorString(error) << "\n";
279 return false;
280}
281
282bool KernelUtility::CheckOptions(void) {
Gaurav Shah528a2c12010-03-18 13:10:10 -0700283 // Ensure that only one of --{describe|generate|verify} is set.
284 if (!((is_describe_ && !is_generate_ && !is_verify_) ||
285 (!is_describe_ && is_generate_ && !is_verify_) ||
286 (!is_describe_ && !is_generate_ && is_verify_))) {
287 cerr << "One (and only one) of --describe, --generate or --verify "
288 << "must be specified.\n";
Gaurav Shah80d129b2010-03-03 17:58:43 -0800289 return false;
290 }
291 // Common required options.
292 if (in_file_.empty()) {
293 cerr << "No input file specified.\n";
294 return false;
295 }
296 // Required options for --verify.
297 if (is_verify_ && firmware_key_pub_file_.empty()) {
298 cerr << "No pre-processed public firmware key file specified.\n";
299 return false;
300 }
301 // Required options for --generate.
302 if (is_generate_) {
303 if (firmware_key_file_.empty()) {
304 cerr << "No firmware key file specified.\n";
305 return false;
306 }
307 if (kernel_key_file_.empty()) {
308 cerr << "No kernel key file specified.\n";
309 return false;
310 }
311 if (kernel_key_pub_file_.empty()) {
312 cerr << "No pre-processed public kernel key file specified\n";
313 return false;
314 }
315 if (kernel_key_version_ <= 0 || kernel_key_version_ > UINT16_MAX) {
316 cerr << "Invalid or no kernel key version specified.\n";
317 return false;
318 }
319 if (firmware_sign_algorithm_ < 0 ||
320 firmware_sign_algorithm_ >= kNumAlgorithms) {
321 cerr << "Invalid or no firmware signing key algorithm specified.\n";
322 return false;
323 }
324 if (kernel_sign_algorithm_ < 0 ||
325 kernel_sign_algorithm_ >= kNumAlgorithms) {
326 cerr << "Invalid or no kernel signing key algorithm specified.\n";
327 return false;
328 }
329 if (kernel_version_ <=0 || kernel_version_ > UINT16_MAX) {
330 cerr << "Invalid or no kernel version specified.\n";
331 return false;
332 }
333 if (out_file_.empty()) {
334 cerr <<"No output file specified.\n";
335 return false;
336 }
337 }
338 return true;
339}
340
341} // namespace vboot_reference
342
343int main(int argc, char* argv[]) {
Gaurav Shah528a2c12010-03-18 13:10:10 -0700344 vboot_reference::KernelUtility ku;
345 if (!ku.ParseCmdLineOptions(argc, argv)) {
346 ku.PrintUsage();
Gaurav Shah80d129b2010-03-03 17:58:43 -0800347 return -1;
348 }
Gaurav Shah528a2c12010-03-18 13:10:10 -0700349 if (ku.is_describe()) {
350 ku.DescribeSignedImage();
Gaurav Shah80d129b2010-03-03 17:58:43 -0800351 }
Gaurav Shah528a2c12010-03-18 13:10:10 -0700352 else if (ku.is_generate()) {
353 if (!ku.GenerateSignedImage())
354 return -1;
355 ku.OutputSignedImage();
356 }
357 else if (ku.is_verify()) {
Gaurav Shah80d129b2010-03-03 17:58:43 -0800358 cerr << "Verification ";
Gaurav Shah528a2c12010-03-18 13:10:10 -0700359 if (ku.VerifySignedImage())
Gaurav Shah80d129b2010-03-03 17:58:43 -0800360 cerr << "SUCCESS.\n";
361 else
362 cerr << "FAILURE.\n";
363 }
364 return 0;
365}