Files
OpenCellular/utility/kernel_utility.cc
Gaurav Shah bd52fc793a VBoot Reference: Make kernel_config a 4K byte block, and move it after the verified boot block.
The kernel_config is now stored as a 4K binary block instead of the kconfig_options structure that was being used before. Since the verified boot code doesn't care what kernel config options are (other than the length of the kernel image and for verifying them before the rest of kernel), it is ok to keep them as a blackbox.

This CL also changes the verified boot kernel layout - VBlock Data followed by Kernel Config followed by the Kernel Image. This will allow them to be stored separately, or as a concatenated block (for easy memory mapping during kernel load). This should ease the process of generating a layout for verified boot kernel images which is also compatible with legacy BIOSes that don't support this mechanism.

Finally, there is also a new firmware API function to determine the size of a kernel verified boot block, given a pointer to its beginning (for determining the offset to the kernel config and data).

Review URL: http://codereview.chromium.org/1732022
2010-04-29 15:30:25 -07:00

354 lines
11 KiB
C++

// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Utility for manipulating verified boot kernel images.
//
#include "kernel_utility.h"
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdint.h> // Needed for UINT16_MAX.
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
extern "C" {
#include "cryptolib.h"
#include "file_keys.h"
#include "kernel_image.h"
#include "utility.h"
}
extern int errno;
using std::cerr;
namespace vboot_reference {
KernelUtility::KernelUtility(): image_(NULL),
firmware_key_pub_(NULL),
header_version_(1),
firmware_sign_algorithm_(-1),
kernel_sign_algorithm_(-1),
kernel_key_version_(-1),
kernel_version_(-1),
kernel_len_(0),
kernel_config_(NULL),
is_generate_(false),
is_verify_(false),
is_describe_(false),
is_only_vblock_(false) {
}
KernelUtility::~KernelUtility() {
Free(kernel_config_);
RSAPublicKeyFree(firmware_key_pub_);
KernelImageFree(image_);
}
void KernelUtility::PrintUsage(void) {
cerr <<
"Utility to generate/verify/describe a verified boot kernel image\n\n"
"Usage: kernel_utility <--generate|--verify|--describe> [OPTIONS]\n\n"
"For \"--verify\", required OPTIONS are:\n"
"--in <infile>\t\t\tVerified boot kernel image to verify.\n"
"--firmware_key_pub <pubkeyfile>\tPre-processed public firmware key "
"to use for verification.\n\n"
"For \"--generate\", required OPTIONS are:\n"
"--firmware_key <privkeyfile>\tPrivate firmware signing key file\n"
"--kernel_key <privkeyfile>\tPrivate kernel signing key file\n"
"--kernel_key_pub <pubkeyfile>\tPre-processed public kernel signing"
" key\n"
"--firmware_sign_algorithm <algoid>\tSigning algorithm used by "
"the firmware\n"
"--kernel_sign_algorithm <algoid>\tSigning algorithm to use for kernel\n"
"--kernel_key_version <version#>\tKernel signing Key Version#\n"
"--kernel_version <version#>\tKernel Version#\n"
"--in <infile>\t\tKernel Image to sign\n"
"--out <outfile>\t\tOutput file for verified boot Kernel image\n\n"
"Optional arguments for \"--generate\" include:\n"
"--config <file>\t\t\tPopulate contents of kernel config from a file\n"
"--vblock\t\t\tJust output the verification block\n\n"
"<algoid> (for --*_sign_algorithm) is one of the following:\n";
for (int i = 0; i < kNumAlgorithms; i++) {
cerr << i << " for " << algo_strings[i] << "\n";
}
cerr << "\n\n";
}
bool KernelUtility::ParseCmdLineOptions(int argc, char* argv[]) {
int option_index;
static struct option long_options[] = {
{"firmware_key", 1, 0, 0},
{"firmware_key_pub", 1, 0, 0},
{"kernel_key", 1, 0, 0},
{"kernel_key_pub", 1, 0, 0},
{"firmware_sign_algorithm", 1, 0, 0},
{"kernel_sign_algorithm", 1, 0, 0},
{"kernel_key_version", 1, 0, 0},
{"kernel_version", 1, 0, 0},
{"in", 1, 0, 0},
{"out", 1, 0, 0},
{"generate", 0, 0, 0},
{"verify", 0, 0, 0},
{"describe", 0, 0, 0},
{"config", 1, 0, 0},
{"vblock", 0, 0, 0},
{NULL, 0, 0, 0}
};
while (1) {
int i = getopt_long(argc, argv, "", long_options, &option_index);
if (-1 == i) // Done with option processing.
break;
if ('?' == i) // Invalid option found.
return false;
if (0 == i) {
switch (option_index) {
case 0: // firmware_key
firmware_key_file_ = optarg;
break;
case 1: // firmware_key_pub
firmware_key_pub_file_ = optarg;
break;
case 2: // kernel_key
kernel_key_file_ = optarg;
break;
case 3: // kernel_key_pub
kernel_key_pub_file_ = optarg;
break;
case 4: // firmware_sign_algorithm
errno = 0; // strtol() returns an error via errno
firmware_sign_algorithm_ = strtol(optarg,
reinterpret_cast<char**>(NULL), 10);
if (errno)
return false;
break;
case 5: // kernel_sign_algorithm
errno = 0;
kernel_sign_algorithm_ = strtol(optarg,
reinterpret_cast<char**>(NULL), 10);
if (errno)
return false;
break;
case 6: // kernel_key_version
errno = 0;
kernel_key_version_ = strtol(optarg,
reinterpret_cast<char**>(NULL), 10);
if (errno)
return false;
break;
case 7: // kernel_version
errno = 0;
kernel_version_ = strtol(optarg,
reinterpret_cast<char**>(NULL), 10);
if (errno)
return false;
break;
case 8: // in
in_file_ = optarg;
break;
case 9: // out
out_file_ = optarg;
break;
case 10: // generate
is_generate_ = true;
break;
case 11: // verify
is_verify_ = true;
break;
case 12: // describe
is_describe_ = true;
break;
case 13: // config
config_file_ = optarg;
break;
case 14: // vblock
is_only_vblock_ = true;
break;
}
}
}
return CheckOptions();
}
void KernelUtility::OutputSignedImage(void) {
if (image_) {
if (!WriteKernelImage(out_file_.c_str(), image_, is_only_vblock_)) {
cerr << "Couldn't write verified boot kernel image to file "
<< out_file_ <<".\n";
}
}
}
void KernelUtility::DescribeSignedImage(void) {
image_ = ReadKernelImage(in_file_.c_str());
if (!image_) {
cerr << "Couldn't read kernel image or malformed image.\n";
return;
}
PrintKernelImage(image_);
}
bool KernelUtility::GenerateSignedImage(void) {
uint64_t len;
uint64_t kernel_key_pub_len;
image_ = KernelImageNew();
Memcpy(image_->magic, KERNEL_MAGIC, KERNEL_MAGIC_SIZE);
// TODO(gauravsh): make this a command line option.
image_->header_version = 1;
image_->firmware_sign_algorithm = (uint16_t) firmware_sign_algorithm_;
// Copy pre-processed public signing key.
image_->kernel_sign_algorithm = (uint16_t) kernel_sign_algorithm_;
image_->kernel_sign_key = BufferFromFile(kernel_key_pub_file_.c_str(),
&kernel_key_pub_len);
if (!image_->kernel_sign_key)
return false;
image_->kernel_key_version = kernel_key_version_;
// Update header length.
image_->header_len = GetKernelHeaderLen(image_);
// Calculate header checksum.
CalculateKernelHeaderChecksum(image_, image_->header_checksum);
image_->kernel_version = kernel_version_;
if (!config_file_.empty()) {
kernel_config_ = BufferFromFile(config_file_.c_str(), &len);
if (len >= sizeof(image_->kernel_config)) {
cerr << "Input kernel config file is too big!";
return false;
}
Memcpy(image_->kernel_config,
kernel_config_, len);
} else {
Memset(image_->kernel_config, 0,
sizeof(image_->kernel_config));
}
image_->kernel_data = BufferFromFile(in_file_.c_str(),
&image_->kernel_len);
if (!image_)
return false;
// Generate and add the signatures.
if (!AddKernelKeySignature(image_, firmware_key_file_.c_str())) {
cerr << "Couldn't write key signature to verified boot kernel image.\n";
return false;
}
if (!AddKernelSignature(image_, kernel_key_file_.c_str())) {
cerr << "Couldn't write firmware signature to verified boot kernel image.\n";
return false;
}
return true;
}
bool KernelUtility::VerifySignedImage(void) {
int error;
firmware_key_pub_ = RSAPublicKeyFromFile(firmware_key_pub_file_.c_str());
image_ = ReadKernelImage(in_file_.c_str());
if (!firmware_key_pub_) {
cerr << "Couldn't read pre-processed public root key.\n";
return false;
}
if (!image_) {
cerr << "Couldn't read kernel image or malformed image.\n";
return false;
}
if (!(error = VerifyKernelImage(firmware_key_pub_, image_, 0)))
return true;
cerr << VerifyKernelErrorString(error) << "\n";
return false;
}
bool KernelUtility::CheckOptions(void) {
// Ensure that only one of --{describe|generate|verify} is set.
if (!((is_describe_ && !is_generate_ && !is_verify_) ||
(!is_describe_ && is_generate_ && !is_verify_) ||
(!is_describe_ && !is_generate_ && is_verify_))) {
cerr << "One (and only one) of --describe, --generate or --verify "
<< "must be specified.\n";
return false;
}
// Common required options.
if (in_file_.empty()) {
cerr << "No input file specified.\n";
return false;
}
// Required options for --verify.
if (is_verify_ && firmware_key_pub_file_.empty()) {
cerr << "No pre-processed public firmware key file specified.\n";
return false;
}
// Required options for --generate.
if (is_generate_) {
if (firmware_key_file_.empty()) {
cerr << "No firmware key file specified.\n";
return false;
}
if (kernel_key_file_.empty()) {
cerr << "No kernel key file specified.\n";
return false;
}
if (kernel_key_pub_file_.empty()) {
cerr << "No pre-processed public kernel key file specified\n";
return false;
}
if (kernel_key_version_ <= 0 || kernel_key_version_ > UINT16_MAX) {
cerr << "Invalid or no kernel key version specified.\n";
return false;
}
if (firmware_sign_algorithm_ < 0 ||
firmware_sign_algorithm_ >= kNumAlgorithms) {
cerr << "Invalid or no firmware signing key algorithm specified.\n";
return false;
}
if (kernel_sign_algorithm_ < 0 ||
kernel_sign_algorithm_ >= kNumAlgorithms) {
cerr << "Invalid or no kernel signing key algorithm specified.\n";
return false;
}
if (kernel_version_ <=0 || kernel_version_ > UINT16_MAX) {
cerr << "Invalid or no kernel version specified.\n";
return false;
}
if (out_file_.empty()) {
cerr <<"No output file specified.\n";
return false;
}
}
return true;
}
} // namespace vboot_reference
int main(int argc, char* argv[]) {
vboot_reference::KernelUtility ku;
if (!ku.ParseCmdLineOptions(argc, argv)) {
ku.PrintUsage();
return -1;
}
if (ku.is_describe()) {
ku.DescribeSignedImage();
}
else if (ku.is_generate()) {
if (!ku.GenerateSignedImage())
return -1;
ku.OutputSignedImage();
}
else if (ku.is_verify()) {
cerr << "Verification ";
if (ku.VerifySignedImage())
cerr << "SUCCESS.\n";
else
cerr << "FAILURE.\n";
}
return 0;
}