diff options
author | Gaurav Shah <gauravsh@chromium.org> | 2010-03-31 13:26:55 -0700 |
---|---|---|
committer | Gaurav Shah <gauravsh@chromium.org> | 2010-03-31 13:26:55 -0700 |
commit | fc70d72aaab4d558e39ec43832375267603bfd93 (patch) | |
tree | 2b71e90cdb26c079a76ab82607e9bf7a7c97e6ff /utility | |
parent | 5411c7a9f03f91bf2c1cd1cf852db9d4585a05c9 (diff) | |
download | vboot-fc70d72aaab4d558e39ec43832375267603bfd93.tar.gz |
VBoot Reference: Refactoring Part 3
Refactor and restructure reference code into individual self-contain modules. I have revamped the way the code is structured to make it easy to determine which parts belong in the firmware and which are used by userland tools.
common/ - common utilities and stub functions (Firmware)
cryptolib/ - crypto library (Firmware)
misclibs/ - miscellaneous userland libraries (Userland)
sctips/ - Miscellaenous scripts (Userland)
tests/ - Tests (Userland)
vfirmware/ - Verified Firmware Implementation
vfirmware/firmware_image_fw.c (Firmware)
vfirmware/firmware_image.c (Userland)
vkernel/ - Verified Kernel Implementation
vkernel/kernel_image_fw.c (Firmware)
vkernel/kernel_image.c (Userland)
Review URL: http://codereview.chromium.org/1581005
Diffstat (limited to 'utility')
-rw-r--r-- | utility/Makefile | 52 | ||||
-rw-r--r-- | utility/dumpRSAPublicKey.c | 181 | ||||
-rw-r--r-- | utility/firmware_utility.cc | 313 | ||||
-rw-r--r-- | utility/include/firmware_utility.h | 71 | ||||
-rw-r--r-- | utility/include/kernel_utility.h | 79 | ||||
-rw-r--r-- | utility/include/verify_data.h | 20 | ||||
-rw-r--r-- | utility/kernel_utility.cc | 363 | ||||
-rw-r--r-- | utility/signature_digest_utility.c | 54 | ||||
-rw-r--r-- | utility/verify_data.c | 95 |
9 files changed, 1228 insertions, 0 deletions
diff --git a/utility/Makefile b/utility/Makefile new file mode 100644 index 00000000..a5f6f584 --- /dev/null +++ b/utility/Makefile @@ -0,0 +1,52 @@ +# 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. + +TOP ?= ../ +CC ?= gcc +CXX ?= g++ +INCLUDES += -I./include \ + -I../cryptolib/include \ + -I../common/include \ + -I../misclibs/include \ + -I../vfirmware/include\ + -I../vkernel/include +CFLAGS ?= -Wall -DNDEBUG -O3 -Werror $(INCLUDES) +LIBS = $(TOP)/misclibs/file_keys.o \ + $(TOP)/misclibs/signature_digest.o \ + $(TOP)/vfirmware/firmware_image.o \ + $(TOP)/vfirmware/firmware_image_fw.o \ + $(TOP)/vkernel/kernel_image.o \ + $(TOP)/vkernel/kernel_image_fw.o +FIRMWARELIBS = $(TOP)/cryptolib/libcrypto.a $(TOP)/common/libcommon.a + +TARGET_BINS = dumpRSAPublicKey \ + firmware_utility \ + kernel_utility \ + signature_digest_utility \ + verify_data + +all: $(TARGET_BINS) + +dumpRSAPublicKey: dumpRSAPublicKey.c + $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ -lcrypto + +firmware_utility: firmware_utility.cc $(LIBS) $(FIRMWARELIBS) + $(CXX) $(CFLAGS) $(INCLUDES) -ggdb -D__STDC_LIMIT_MACROS $< \ + -o $@ $(FIRMWARELIBS) $(LIBS) $(TOP)/common/libcommon.a \ + -lcrypto + +kernel_utility: kernel_utility.cc $(LIBS) $(FIRMWARELIBS) + $(CXX) $(CFLAGS) $(INCLUDES) -ggdb -D__STDC_LIMIT_MACROS $< \ + -o $@ $(FIRMWARELIBS) $(LIBS) $(TOP)/common/libcommon.a \ + -lcrypto + +signature_digest_utility: signature_digest_utility.c $(LIBS) $(FIRMWARELIBS) + $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FIRMWARELIBS) -lcrypto + +verify_data: verify_data.c $(LIBS) $(FIRMWARELIBS) + $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FIRMWARELIBS) -lcrypto + +clean: + rm -f $(TARGET_BINS) + diff --git a/utility/dumpRSAPublicKey.c b/utility/dumpRSAPublicKey.c new file mode 100644 index 00000000..837303cb --- /dev/null +++ b/utility/dumpRSAPublicKey.c @@ -0,0 +1,181 @@ +/* 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. + */ + +/* C port of DumpPublicKey.java from the Android Open source project with + * support for additional RSA key sizes. (platform/system/core,git/libmincrypt + * /tools/DumpPublicKey.java). Uses the OpenSSL X509 and BIGNUM library. + */ + +#include <stdint.h> +#include <openssl/bn.h> +#include <openssl/evp.h> +#include <openssl/pem.h> +#include <openssl/x509.h> +#include <string.h> +#include <unistd.h> + +/* Command line tool to extract RSA public keys from X.509 certificates + * and output a pre-processed version of keys for use by RSA verification + * routines. + */ + +int check(RSA* key) { + int public_exponent = BN_get_word(key->e); + int modulus = BN_num_bits(key->n); + + if (public_exponent != 65537) { + fprintf(stderr, "WARNING: Public exponent should be 65537 (but is %d).\n", + public_exponent); + } + + if (modulus != 1024 && modulus != 2048 && modulus != 4096 + && modulus != 8192) { + fprintf(stderr, "ERROR: Unknown modulus length = %d.\n", modulus); + return 0; + } + return 1; +} + +/* Pre-processes and outputs RSA public key to standard out. + */ +void output(RSA* key) { + int i, nwords; + BIGNUM *N = key->n; + BIGNUM *Big1 = NULL, *Big2 = NULL, *Big32 = NULL, *BigMinus1 = NULL; + BIGNUM *B = NULL; + BIGNUM *N0inv= NULL, *R = NULL, *RR = NULL, *RRTemp = NULL, *NnumBits = NULL; + BIGNUM *n = NULL, *rr = NULL; + BN_CTX *bn_ctx = BN_CTX_new(); + uint32_t n0invout; + + N = key->n; + /* Output size of RSA key in 32-bit words */ + nwords = BN_num_bits(N) / 32; + if (-1 == write(1, &nwords, sizeof(nwords))) + goto failure; + + + /* Initialize BIGNUMs */ + Big1 = BN_new(); + Big2 = BN_new(); + Big32 = BN_new(); + BigMinus1 = BN_new(); + N0inv= BN_new(); + R = BN_new(); + RR = BN_new(); + RRTemp = BN_new(); + NnumBits = BN_new(); + n = BN_new(); + rr = BN_new(); + + + BN_set_word(Big1, 1L); + BN_set_word(Big2, 2L); + BN_set_word(Big32, 32L); + BN_sub(BigMinus1, Big1, Big2); + + B = BN_new(); + BN_exp(B, Big2, Big32, bn_ctx); /* B = 2^32 */ + + /* Calculate and output N0inv = -1 / N[0] mod 2^32 */ + BN_mod_inverse(N0inv, N, B, bn_ctx); + BN_sub(N0inv, B, N0inv); + n0invout = BN_get_word(N0inv); + if (-1 == write(1, &n0invout, sizeof(n0invout))) + goto failure; + + /* Calculate R = 2^(# of key bits) */ + BN_set_word(NnumBits, BN_num_bits(N)); + BN_exp(R, Big2, NnumBits, bn_ctx); + + /* Calculate RR = R^2 mod N */ + BN_copy(RR, R); + BN_mul(RRTemp, RR, R, bn_ctx); + BN_mod(RR, RRTemp, N, bn_ctx); + + + /* Write out modulus as little endian array of integers. */ + for (i = 0; i < nwords; ++i) { + uint32_t nout; + + BN_mod(n, N, B, bn_ctx); /* n = N mod B */ + nout = BN_get_word(n); + if (-1 == write(1, &nout, sizeof(nout))) + goto failure; + + BN_rshift(N, N, 32); /* N = N/B */ + } + + /* Write R^2 as little endian array of integers. */ + for (i = 0; i < nwords; ++i) { + uint32_t rrout; + + BN_mod(rr, RR, B, bn_ctx); /* rr = RR mod B */ + rrout = BN_get_word(rr); + if (-1 == write(1, &rrout, sizeof(rrout))) + goto failure; + + BN_rshift(RR, RR, 32); /* RR = RR/B */ + } + +failure: + /* Free BIGNUMs. */ + BN_free(Big1); + BN_free(Big2); + BN_free(Big32); + BN_free(BigMinus1); + BN_free(N0inv); + BN_free(R); + BN_free(RRTemp); + BN_free(NnumBits); + BN_free(n); + BN_free(rr); + +} + +int main(int argc, char* argv[]) { + FILE* fp; + X509* cert = NULL; + RSA* pubkey = NULL; + EVP_PKEY* key; + + if (argc != 2) { + fprintf(stderr, "Usage: %s <certfile>\n", argv[0]); + return -1; + } + + fp = fopen(argv[1], "r"); + + if (!fp) { + fprintf(stderr, "Couldn't open certificate file!\n"); + return -1; + } + + /* Read the certificate */ + if (!PEM_read_X509(fp, &cert, NULL, NULL)) { + fprintf(stderr, "Couldn't read certificate.\n"); + goto fail; + } + + /* Get the public key from the certificate. */ + key = X509_get_pubkey(cert); + + /* Convert to a RSA_style key. */ + if (!(pubkey = EVP_PKEY_get1_RSA(key))) { + fprintf(stderr, "Couldn't convert to a RSA style key.\n"); + goto fail; + } + + if (check(pubkey)) { + output (pubkey); + } + +fail: + X509_free(cert); + RSA_free(pubkey); + fclose(fp); + + return 0; +} diff --git a/utility/firmware_utility.cc b/utility/firmware_utility.cc new file mode 100644 index 00000000..85275e73 --- /dev/null +++ b/utility/firmware_utility.cc @@ -0,0 +1,313 @@ +// 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 firmware images. +// + +#include "firmware_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 "firmware_image.h" +#include "utility.h" +} + +extern int errno; +using std::cerr; + +namespace vboot_reference { + +FirmwareUtility::FirmwareUtility(): + image_(NULL), + root_key_pub_(NULL), + firmware_version_(-1), + firmware_key_version_(-1), + firmware_sign_algorithm_(-1), + is_generate_(false), + is_verify_(false), + is_describe_(false) { +} + +FirmwareUtility::~FirmwareUtility() { + RSAPublicKeyFree(root_key_pub_); + FirmwareImageFree(image_); +} + +void FirmwareUtility::PrintUsage(void) { + cerr << + "Utility to generate/verify a verified boot firmware image\n\n" + "Usage: firmware_utility <--generate|--verify> [OPTIONS]\n\n" + "For \"--verify\", required OPTIONS are:\n" + "--in <infile>\t\t\tVerified boot firmware image to verify.\n" + "--root_key_pub <pubkeyfile>\tPre-processed public root key " + "to use for verification.\n\n" + "For \"--generate\", required OPTIONS are:\n" + "--root_key <privkeyfile>\tPrivate root key file\n" + "--firmware_sign_key <privkeyfile>\tPrivate signing key file\n" + "--firmware_sign_key_pub <pubkeyfile>\tPre-processed public signing" + " key\n" + "--firmware_sign_algorithm <algoid>\tSigning algorithm to use\n" + "--firmware_key_version <version#>\tSigning Key Version#\n" + "--firmware_version <version#>\tFirmware Version#\n" + "--in <infile>\t\t\tFirmware Image to sign\n" + "--out <outfile>\t\t\tOutput file for verified boot firmware image\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 FirmwareUtility::ParseCmdLineOptions(int argc, char* argv[]) { + int option_index; + static struct option long_options[] = { + {"root_key", 1, 0, 0}, + {"root_key_pub", 1, 0, 0}, + {"firmware_sign_key", 1, 0, 0}, + {"firmware_sign_key_pub", 1, 0, 0}, + {"firmware_sign_algorithm", 1, 0, 0}, + {"firmware_key_version", 1, 0, 0}, + {"firmware_version", 1, 0, 0}, + {"in", 1, 0, 0}, + {"out", 1, 0, 0}, + {"generate", 0, 0, 0}, + {"verify", 0, 0, 0}, + {"describe", 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: // root_key + root_key_file_ = optarg; + break; + case 1: // root_key_pub + root_key_pub_file_ = optarg; + break; + case 2: // firmware_sign_key + firmware_key_file_ = optarg; + break; + case 3: // firmware_sign_key_pub + firmware_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: // firmware_key_version + errno = 0; + firmware_key_version_ = strtol(optarg, + reinterpret_cast<char**>(NULL), 10); + if (errno) + return false; + break; + case 6: // firmware_version + errno = 0; + firmware_version_ = strtol(optarg, + reinterpret_cast<char**>(NULL), 10); + if (errno) + return false; + break; + case 7: // in + in_file_ = optarg; + break; + case 8: // out + out_file_ = optarg; + break; + case 9: // generate + is_generate_ = true; + break; + case 10: // verify + is_verify_ = true; + break; + case 11: // describe + is_describe_ = true; + break; + } + } + } + return CheckOptions(); +} + + +void FirmwareUtility::OutputSignedImage(void) { + if (image_) { + if (!WriteFirmwareImage(out_file_.c_str(), image_)) { + cerr << "Couldn't write verified boot image to file " + << out_file_ <<".\n"; + } + } +} + +void FirmwareUtility::DescribeSignedImage(void) { + image_ = ReadFirmwareImage(in_file_.c_str()); + if (!image_) { + cerr << "Couldn't read firmware image or malformed image.\n"; + } + PrintFirmwareImage(image_); +} + +bool FirmwareUtility::GenerateSignedImage(void) { + uint64_t firmware_sign_key_pub_len; + image_ = FirmwareImageNew(); + + Memcpy(image_->magic, FIRMWARE_MAGIC, FIRMWARE_MAGIC_SIZE); + + // Copy pre-processed public signing key. + image_->firmware_sign_algorithm = (uint16_t) firmware_sign_algorithm_; + image_->firmware_sign_key = BufferFromFile( + firmware_key_pub_file_.c_str(), + &firmware_sign_key_pub_len); + if (!image_->firmware_sign_key) + return false; + image_->firmware_key_version = firmware_key_version_; + + // Update header length. + image_->header_len = GetFirmwareHeaderLen(image_); + + // Calculate header checksum. + CalculateFirmwareHeaderChecksum(image_, image_->header_checksum); + + image_->firmware_version = firmware_version_; + image_->firmware_len = 0; + // TODO(gauravsh): Populate this with the right bytes once we decide + // what goes into the preamble. + Memset(image_->preamble, 'P', FIRMWARE_PREAMBLE_SIZE); + image_->firmware_data = BufferFromFile(in_file_.c_str(), + &image_->firmware_len); + if (!image_) + return false; + // Generate and add the signatures. + if (!AddFirmwareKeySignature(image_, root_key_file_.c_str())) { + cerr << "Couldn't write key signature to verified boot image.\n"; + return false; + } + + if (!AddFirmwareSignature(image_, firmware_key_file_.c_str())) { + cerr << "Couldn't write firmware signature to verified boot image.\n"; + return false; + } + return true; +} + +bool FirmwareUtility::VerifySignedImage(void) { + int error; + root_key_pub_ = RSAPublicKeyFromFile(root_key_pub_file_.c_str()); + image_ = ReadFirmwareImage(in_file_.c_str()); + + if (!root_key_pub_) { + cerr << "Couldn't read pre-processed public root key.\n"; + return false; + } + + if (!image_) { + cerr << "Couldn't read firmware image or malformed image.\n"; + return false; + } + if (VERIFY_FIRMWARE_SUCCESS == + (error = VerifyFirmwareImage(root_key_pub_, image_))) + return true; + cerr << VerifyFirmwareErrorString(error) << "\n"; + return false;; +} + +bool FirmwareUtility::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_ && root_key_pub_file_.empty()) { + cerr << "No pre-processed public root key file specified." << "\n"; + return false; + } + // Required options for --generate. + if (is_generate_) { + if (root_key_file_.empty()) { + cerr << "No root key file specified." << "\n"; + return false; + } + if (firmware_version_ <= 0 || firmware_version_ > UINT16_MAX) { + cerr << "Invalid or no firmware version specified." << "\n"; + return false; + } + if (firmware_key_file_.empty()) { + cerr << "No signing key file specified." << "\n"; + return false; + } + if (firmware_key_pub_file_.empty()) { + cerr << "No pre-processed public signing key file specified." << "\n"; + return false; + } + if (firmware_key_version_ <= 0 || firmware_key_version_ > UINT16_MAX) { + cerr << "Invalid or no key version specified." << "\n"; + return false; + } + if (firmware_sign_algorithm_ < 0 || + firmware_sign_algorithm_ >= kNumAlgorithms) { + cerr << "Invalid or no signing key algorithm 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::FirmwareUtility fu; + if (!fu.ParseCmdLineOptions(argc, argv)) { + fu.PrintUsage(); + return -1; + } + if (fu.is_describe()) { + fu.DescribeSignedImage(); + } + if (fu.is_generate()) { + if (!fu.GenerateSignedImage()) + return -1; + fu.OutputSignedImage(); + } + if (fu.is_verify()) { + cerr << "Verification "; + if (fu.VerifySignedImage()) + cerr << "SUCCESS.\n"; + else + cerr << "FAILURE.\n"; + } + return 0; +} diff --git a/utility/include/firmware_utility.h b/utility/include/firmware_utility.h new file mode 100644 index 00000000..6c2b735a --- /dev/null +++ b/utility/include/firmware_utility.h @@ -0,0 +1,71 @@ +// 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. + +#ifndef VBOOT_REFERENCE_FIRMWARE_UTILITY_H_ +#define VBOOT_REFERENCE_FIRMWARE_UTILITY_H_ + +#include <string> + +class FirmwareImage; +struct RSAPublicKey; + +namespace vboot_reference { + +// A class for handling verified boot firmware images. +class FirmwareUtility { + public: + FirmwareUtility(); + ~FirmwareUtility(); + + // Print usage to stderr. + void PrintUsage(void); + + // Parse command line options and populate data members. + // Return true on success, false on failure. + bool ParseCmdLineOptions(int argc, char* argv[]); + + // Print descriptio of verified boot firmware image. + void DescribeSignedImage(); + + // Generate a verified boot image by reading firmware data from in_file_. + // Return true on success, false on failure. + bool GenerateSignedImage(); + + // Verify a previously generated signed firmware image using the root key read + // from [root_key_pub_file_]. + bool VerifySignedImage(); + + // Output the verified boot image to out_file_. + void OutputSignedImage(); + + + bool is_generate() { return is_generate_; } + bool is_verify() { return is_verify_; } + bool is_describe() { return is_describe_; } + + private: + + // Check if all options were specified and sane. + // Return true on success, false on failure. + bool CheckOptions(); + + FirmwareImage* image_; + RSAPublicKey* root_key_pub_; + std::string root_key_file_; + std::string root_key_pub_file_; + int firmware_version_; + std::string firmware_key_file_; + std::string firmware_key_pub_file_; + int firmware_key_version_; + int firmware_sign_algorithm_; + std::string in_file_; + std::string out_file_; + bool is_generate_; // Are we generating a new image? + bool is_verify_; // Are we just verifying an already signed image? + bool is_describe_; // Should we print out description of the image? +}; + +} // namespace vboot_reference + +#endif // VBOOT_REFERENCE_FIRMWARE_UTILITY_H_ diff --git a/utility/include/kernel_utility.h b/utility/include/kernel_utility.h new file mode 100644 index 00000000..1cb7f1ae --- /dev/null +++ b/utility/include/kernel_utility.h @@ -0,0 +1,79 @@ +// 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. + +#ifndef VBOOT_REFERENCE_KERNEL_UTILITY_H_ +#define VBOOT_REFERENCE_KERNEL_UTILITY_H_ + +#include <string> + +extern "C" { +#include "kernel_image.h" +} + +struct RSAPublicKey; + +namespace vboot_reference { + +// A class for handling verified boot kernel images. +class KernelUtility { + public: + KernelUtility(); + ~KernelUtility(); + + // Print usage to stderr. + void PrintUsage(void); + + // Parse command line options and populate data members. + // Return true on success, false on failure. + bool ParseCmdLineOptions(int argc, char* argv[]); + + // Print description of a verified boot kernel image. + void DescribeSignedImage(); + + // Generate a verified boot image by reading kernel data from in_file_. + // Return true on success, false on failure. + bool GenerateSignedImage(); + + // Verify a previously generated signed firmware image using the key read + // from [firmware_key_pub_file_]. + bool VerifySignedImage(); + + // Output the verified boot kernel image to out_file_. + void OutputSignedImage(); + + bool is_generate() { return is_generate_; } + bool is_verify() { return is_verify_; } + bool is_describe() { return is_describe_; } + + private: + + // Check if all options were specified and sane. + // Return true on success, false on failure. + bool CheckOptions(); + + KernelImage* image_; + RSAPublicKey* firmware_key_pub_; // Root key used for verification. + std::string firmware_key_file_; // Private key for signing the kernel key. + std::string firmware_key_pub_file_; + std::string kernel_key_file_; // Private key for signing the kernel. + std::string kernel_key_pub_file_; + + // Fields of a KernelImage. (read from the command line). + int header_version_; + int firmware_sign_algorithm_; + int kernel_sign_algorithm_; + int kernel_key_version_; + int kernel_version_; + kconfig_options options_; + + std::string in_file_; + std::string out_file_; + bool is_generate_; // Are we generating a new image? + bool is_verify_; // Are we just verifying an already signed image? + bool is_describe_; // Should we print out description of the image? +}; + +} // namespace vboot_reference + +#endif // VBOOT_REFERENCE_FIRMWARE_UTILITY_H_ diff --git a/utility/include/verify_data.h b/utility/include/verify_data.h new file mode 100644 index 00000000..51b9fd6d --- /dev/null +++ b/utility/include/verify_data.h @@ -0,0 +1,20 @@ +/* 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. + */ + +#ifndef VBOOT_REFERENCE_VERIFY_DATA_H_ +#define VBOOT_REFERENCE_VERIFY_DATA_H_ + +/* Reads a pre-processed key from [input_file] and + * returns it in a RSAPublicKey structure. + * Caller owns the returned key and must free it. + */ +RSAPublicKey* read_RSAkey(char *input_file); + +/* Return a signature of [len] bytes read from [input_file]. + * Caller owns the returned signature and must free it. + */ +uint8_t* read_signature(char *input_file, int len); + +#endif /* VBOOT_REFERENCE_VERIFY_DATA_H_ */ diff --git a/utility/kernel_utility.cc b/utility/kernel_utility.cc new file mode 100644 index 00000000..9fedeb5f --- /dev/null +++ b/utility/kernel_utility.cc @@ -0,0 +1,363 @@ +// 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), + is_generate_(false), + is_verify_(false), + is_describe_(false){ + // Populate kernel config options with defaults. + options_.version[0] = 1; + options_.version[1] = 0; + options_.kernel_len = 0; + options_.kernel_load_addr = 0; + options_.kernel_entry_addr = 0; +} + +KernelUtility::~KernelUtility() { + 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_version <version>\n" + "--kernel_load_addr <addr>\n" + "--kernel_entry_addr <addr>\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}, + {"config_version", 1, 0, 0}, + {"kernel_load_addr", 1, 0, 0}, + {"kernel_entry_addr", 1, 0, 0}, + {"describe", 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: // config_version + if (2 != sscanf(optarg, "%d.%d", &options_.version[0], + &options_.version[1])) + return false; + break; + case 13: // kernel_load_addr + errno = 0; + options_.kernel_load_addr = + strtol(optarg, reinterpret_cast<char**>(NULL), 10); + if (errno) + return false; + break; + case 14: // kernel_entry_addr + errno = 0; + options_.kernel_entry_addr = + strtol(optarg, reinterpret_cast<char**>(NULL), 10); + if (errno) + return false; + break; + case 15: // describe + is_describe_ = true; + break; + } + } + } + return CheckOptions(); +} + +void KernelUtility::OutputSignedImage(void) { + if (image_) { + if (!WriteKernelImage(out_file_.c_str(), image_)) { + 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 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_; + image_->options.version[0] = options_.version[0]; + image_->options.version[1] = options_.version[1]; + // TODO(gauravsh): Add a command line option for this. + Memset(image_->options.cmd_line, 0, sizeof(image_->options.cmd_line)); + image_->options.kernel_load_addr = options_.kernel_load_addr; + image_->options.kernel_entry_addr = options_.kernel_entry_addr; + image_->kernel_data = BufferFromFile(in_file_.c_str(), + &image_->options.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; +} diff --git a/utility/signature_digest_utility.c b/utility/signature_digest_utility.c new file mode 100644 index 00000000..6c8891b9 --- /dev/null +++ b/utility/signature_digest_utility.c @@ -0,0 +1,54 @@ +/* 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 that outputs the cryptographic digest of a contents of a + * file in a format that can be directly used to generate PKCS#1 v1.5 + * signatures via the "openssl" command line utility. + */ + + +#include <stdio.h> +#include <stdlib.h> + +#include "file_keys.h" +#include "padding.h" +#include "signature_digest.h" +#include "utility.h" + +int main(int argc, char* argv[]) { + int algorithm = -1; + int error_code = 0; + uint8_t* buf = NULL; + uint8_t* signature_digest = NULL; + uint64_t len; + uint32_t signature_digest_len; + + if (argc != 3) { + fprintf(stderr, "Usage: %s <algoid> <file>", argv[0]); + return -1; + } + algorithm = atoi(argv[1]); + if (algorithm < 0 || algorithm >= kNumAlgorithms) { + fprintf(stderr, "Invalid Algorithm!\n"); + return -1; + } + + buf = BufferFromFile(argv[2], &len); + if (!buf) { + fprintf(stderr, "Could read file: %s\n", argv[2]); + return -1; + } + + signature_digest = SignatureDigest(buf, len, algorithm); + signature_digest_len = (hash_size_map[algorithm] + + digestinfo_size_map[algorithm]); + if (!signature_digest) + error_code = -1; + if(signature_digest && + 1 != fwrite(signature_digest, signature_digest_len, 1, stdout)) + error_code = -1; + Free(signature_digest); + Free(buf); + return error_code; +} diff --git a/utility/verify_data.c b/utility/verify_data.c new file mode 100644 index 00000000..c9da2401 --- /dev/null +++ b/utility/verify_data.c @@ -0,0 +1,95 @@ +/* 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. + */ + +/* Routines for verifying a file's signature. Useful in testing the core + * RSA verification implementation. + */ + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "cryptolib.h" +#include "file_keys.h" +#include "verify_data.h" + +/* ANSI Color coding sequences. */ +#define COL_GREEN "\e[1;32m" +#define COL_RED "\e[0;31m" +#define COL_STOP "\e[m" + +uint8_t* read_signature(char* input_file, int len) { + int i, sigfd; + uint8_t* signature = NULL; + if ((sigfd = open(input_file, O_RDONLY)) == -1) { + fprintf(stderr, "Couldn't open signature file\n"); + return NULL; + } + + /* Read the signature into a buffer*/ + signature = (uint8_t*) malloc(len); + if (!signature) + return NULL; + + if( (i = read(sigfd, signature, len)) != len ) { + fprintf(stderr, "Wrong signature length - Expected = %d, Received = %d\n", + len, i); + close(sigfd); + return NULL; + } + + close(sigfd); + return signature; +} + +int main(int argc, char* argv[]) { + int i, algorithm, sig_len; + int return_code = 1; /* Default to error. */ + uint8_t* digest = NULL; + uint8_t* signature = NULL; + RSAPublicKey* key = NULL; + + if (argc!=5) { + fprintf(stderr, "Usage: %s <algorithm> <key file> <signature file>" + " <input file>\n\n", argv[0]); + fprintf(stderr, "where <algorithm> depends on the signature algorithm" + " used:\n"); + for(i = 0; i<kNumAlgorithms; i++) + fprintf(stderr, "\t%d for %s\n", i, algo_strings[i]); + return -1; + } + + algorithm = atoi(argv[1]); + if (algorithm >= kNumAlgorithms) { + fprintf(stderr, "Invalid Algorithm!\n"); + return 0; + } + /* Length of the RSA Signature/RSA Key */ + sig_len = siglen_map[algorithm]; + if ((key = RSAPublicKeyFromFile(argv[2])) && + (signature = read_signature(argv[3], sig_len)) && + (digest = DigestFile(argv[4], algorithm))) { + if (RSAVerify(key, signature, sig_len, algorithm, digest)) { + return_code = 0; + fprintf(stderr, "Signature Verification " + COL_GREEN "SUCCEEDED" COL_STOP "\n"); + } else { + fprintf(stderr, "Signature Verification " + COL_RED "FAILED" COL_STOP "\n"); + } + } + else + return_code = -1; + + free(key); + free(signature); + free(digest); + + return return_code; +} |